Load libraries

library(here)
library(readr)
library(tidyr)
library(dplyr)
library(lubridate)
library(ggplot2)
library(stringr)
library(purrr)
library(readxl)

Read the data

db_EVA<-read_tsv(here("data", "edited", "db_EVA.csv"))
Aviso: One or more parsing issues, call `problems()` on your data frame for details,
e.g.:
  dat <- vroom(...)
  problems(dat)Rows: 1961807 Columns: 69── Column specification ──────────────────────────────────────────────────────────
Delimiter: "\t"
chr (30): Country, Biblioreference, Nr. table in publ., Nr. relevé in table, C...
dbl (15): PlotObservationID, PlotID, TV2 relevé number, Relevé area (m²), Alti...
lgl (24): RS_DUPL, RS_TIME, Lon_prec, Lat_prec, precision_new, Private, Lon1, ...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Problems (do not affect us)

problems<-problems(db_EVA)
sort(unique(problems$col))
[1] 13 42
names(db_EVA[c(13, 42)])
[1] "Altitude" "RS_TIME" 

Update coordinates

Create new column with old coordinates if new not available, and with new if available.

db_EVA <- db_EVA %>%
  mutate(Lon_updated = ifelse(is.na(Lon_prec),Longitude,Lon_prec),
         Lat_updated = ifelse(is.na(Lat_prec),Latitude,Lat_prec))
print(db_EVA, width = Inf)

ISSUE 2: Coordinates are NA

Coordinates are also NA in some cases.

nrow(db_EVA%>%filter(is.na(Lon_updated) & is.na(Lat_updated )))
[1] 184081

Number of observations

Number of observations for each plot should be 1, as this is EVA (without ReSurvey). I am not sure how to uniquely identify each plot here!

Some dates recorded as 0:00:00: set to NA

ISSUE 5: Datasets with only presence/absence

db_EVA %>%
  filter(`Cover abundance scale`=="Presence/Absence") %>%
  distinct(Dataset)
ggplot(db_EVA %>% 
         mutate(pres_or_ab =ifelse(`Cover abundance scale`=="Presence/Absence",
                                   "Presence/Absence", "Abundance"),
                DK_Naturdata_Res = ifelse(Dataset == "DK_Naturdata_Res",
                                          "Y", "N")),
                aes(pres_or_ab, fill = DK_Naturdata_Res)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage", x = NULL)

# Very small % of DK_Naturdata_Res == "Y" in Presence/Absence 
# but not visible in the figure

For DK_Naturdata_Res - info about habitat from Jerker’s file (see below).

ISSUE 6: Observations with wrong country (TBD in GIS)

ISSUE 7: Different cover abundance scales

ggplot(db_EVA, aes(`Cover abundance scale`)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of EVA (without ReSurvey) observations",
       x = "Cover abundance scale") +
  coord_flip()

Supposing all abundance scales are equivalent.

EUNIS codes

Used this info in metadata file:

Expert system classification to EUNIS habitats (https://zenodo.org/records/4812736 ; https://floraveg.eu/habitat/). I am sending you legend for EUNIS classification version 2022-10-16 with all codes and meanings, directly prepared from expert system file (second sheet) - it is slightly different from published version in ZENODO (https://zenodo.org/records/4812736 , little bit old dated now) and from https://floraveg.eu/habitat/ (little bit newer than in current EVA version).

Qa = mires and Qb = wetlands P units – in floraveg.eu there is slightly different classification (https://floraveg.eu/habitat/overview/P), but in EVA is still this classification of P:

P Surface waters Pa Base-poor spring and spring brook Pb Calcareous spring and spring brook Pc Brackish-water vegetation Pd Fresh-water small pleustophyte vegetation Pe Fresh-water large pleustophyte vegetation Pf Fresh-water submerged vegetation Pg Fresh-water nymphaeid vegetation Ph Oligotrophic-water vegetation Pi Dystrophic-water vegetation Pj Stonewort vegetation

Presence of “!” simply means that for one unit there are two or more different formulas, e.g. R11 and R11!. So it is only technical stuff.

Multiple assignment of relevé – no priority, alphabetical order, e.g. N16!,S66,S81 means that relevé can be assigned to all 3 units: N16 Mediterranean and Macaronesian coastal dune grassland (grey dune), S66 Mediterranean halo-nitrophilous scrub and S81 Canarian xerophytic scrub

No value present in Expert System – relevé didn´t enter expert system classification (= it means that some prerequisites are missing)

“~” – relevé entered expert classification however was not classified to any EUNIS unit +

Clean info on Expert system column and separate it when there are several codes.

db_EVA <- db_EVA %>%
  mutate(
    # Clean 'Expert System' column by removing "!" and replacing "~" with NA
    `Expert System` = case_when(
      `Expert System` == "~" ~ NA_character_,  # Replace "~" with NA
      TRUE ~ str_replace_all(`Expert System`, "!", "")  # Remove "!"
    )
  ) %>%
  # Separate the values in 'Expert System' into multiple columns
  separate(
    `Expert System`,
    into = c("EUNISa", "EUNISb", "EUNISc", "EUNISd"),
    sep = ",",
    extra = "drop",  # Drop extra values if there are more than columns
    fill = "right",   # Fill missing values with NA for cases with fewer values
    remove = FALSE    # Keep the original 'Expert System' column
  )

Calculate how many different EUNIS codes have been assigned:

db_EVA <- db_EVA %>%
  mutate(
    # Count the number of non-NA values across the EUNIS columns
    n_EUNIS = rowSums(!is.na(select(., starts_with("EUNIS"))))
  )
ggplot(db_EVA, aes(n_EUNIS)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of EVA (without ReSurvey) observations",
       x = "Number of differnt EUNIS codes assigned") + coord_flip()

ggplot(db_EVA %>% filter(n_EUNIS > 0), aes(n_EUNIS)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of EVA (without ReSurvey) observations",
       x = "Number of differnt EUNIS codes assigned") + coord_flip()

Correct some EUNIS codes that are probably wrong (copied from ReSurvey notebook):

db_EVA <- db_EVA %>%
  mutate(across(starts_with("EUNIS"), ~ case_when(
    . == "N16M" ~ "N16",
    . == "Sa" ~ "V4",
    . == "Sb" ~ "V5",
    . == "T1CT" ~ "T1C",
    . == "N15A" ~ "N15",
    TRUE ~ .
  )))

Add columns for the different EUNIS levels

db_EVA <- db_EVA %>%
  mutate(
    # EUNISa levels
    EUNISa_1 = substr(EUNISa, 1, ifelse(str_starts(EUNISa, "MA"), 2, 1)),
    EUNISa_2 = ifelse(
      nchar(EUNISa) >= ifelse(str_starts(EUNISa, "MA"), 3, 2), 
      substr(EUNISa, 1, ifelse(str_starts(EUNISa, "MA"), 3, 2)),
      NA_character_
    ),
    EUNISa_3 = ifelse(
      nchar(EUNISa) >= ifelse(str_starts(EUNISa, "MA"), 4, 3), 
      substr(EUNISa, 1, ifelse(str_starts(EUNISa, "MA"), 4, 3)),
      NA_character_
      ),
    EUNISa_4 = ifelse(
      nchar(EUNISa) >= ifelse(str_starts(EUNISa, "MA"), 5, 4), 
      substr(EUNISa, 1, ifelse(str_starts(EUNISa, "MA"), 5, 4)),
      NA_character_
    ),
    
    # EUNISb levels
    EUNISb_1 = substr(EUNISb, 1, ifelse(str_starts(EUNISb, "MA"), 2, 1)),
    EUNISb_2 = ifelse(
      nchar(EUNISb) >= ifelse(str_starts(EUNISb, "MA"), 3, 2), 
      substr(EUNISb, 1, ifelse(str_starts(EUNISb, "MA"), 3, 2)),
      NA_character_
    ),
    EUNISb_3 = ifelse(
      nchar(EUNISb) >= ifelse(str_starts(EUNISb, "MA"), 4, 3), 
      substr(EUNISb, 1, ifelse(str_starts(EUNISb, "MA"), 4, 3)),
      NA_character_
    ),
    EUNISb_4 = ifelse(
      nchar(EUNISb) >= ifelse(str_starts(EUNISb, "MA"), 5, 4), 
      substr(EUNISb, 1, ifelse(str_starts(EUNISb, "MA"), 5, 4)),
      NA_character_
    ),
    
    # EUNISc levels
    EUNISc_1 = substr(EUNISc, 1, ifelse(str_starts(EUNISc, "MA"), 2, 1)),
    EUNISc_2 = ifelse(
      nchar(EUNISc) >= ifelse(str_starts(EUNISc, "MA"), 3, 2), 
      substr(EUNISc, 1, ifelse(str_starts(EUNISc, "MA"), 3, 2)),
      NA_character_
    ),
    EUNISc_3 = ifelse(
      nchar(EUNISc) >= ifelse(str_starts(EUNISc, "MA"), 4, 3), 
      substr(EUNISc, 1, ifelse(str_starts(EUNISc, "MA"), 4, 3)),
      NA_character_
    ),
    EUNISc_4 = ifelse(
      nchar(EUNISc) >= ifelse(str_starts(EUNISc, "MA"), 5, 4), 
      substr(EUNISc, 1, ifelse(str_starts(EUNISc, "MA"), 5, 4)),
      NA_character_
    ),
    
    # EUNISd levels
    EUNISd_1 = substr(EUNISd, 1, ifelse(str_starts(EUNISc, "MA"), 2, 1)),
    EUNISd_2 = ifelse(
      nchar(EUNISd) >= ifelse(str_starts(EUNISd, "MA"), 3, 2), 
      substr(EUNISd, 1, ifelse(str_starts(EUNISd, "MA"), 3, 2)),
      NA_character_
    ),
    EUNISd_3 = ifelse(
      nchar(EUNISd) >= ifelse(str_starts(EUNISd, "MA"), 4, 3), 
      substr(EUNISd, 1, ifelse(str_starts(EUNISd, "MA"), 4, 3)),
      NA_character_
    ),
    EUNISd_4 = ifelse(
      nchar(EUNISd) >= ifelse(str_starts(EUNISd, "MA"), 5, 4), 
      substr(EUNISd, 1, ifelse(str_starts(EUNISd, "MA"), 5, 4)),
      NA_character_
    )
  )

Add descriptions for level 1

db_EVA <- db_EVA %>%
  mutate(
    EUNISa_1_descr = case_when(
      EUNISa_1 == "V" ~ "Vegetated man-made habitats",
      EUNISa_1 == "U" ~ "Inland habitats with no or little soil",
      EUNISa_1 == "T" ~ "Forests and other wooded land",
      EUNISa_1 == "S" ~ "Heathlands, scrub and tundra",
      EUNISa_1 == "R" ~ "Grasslands",
      EUNISa_1 == "Q" ~ "Wetlands",
      EUNISa_1 == "P" ~ "Inland waters",
      EUNISa_1 == "N" ~ "Coastal habitats",
      EUNISa_1 == "MA" ~ "Marine habitats",
      TRUE ~ NA_character_
    ),
    EUNISb_1_descr = case_when(
      EUNISb_1 == "V" ~ "Vegetated man-made habitats",
      EUNISb_1 == "U" ~ "Inland habitats with no or little soil",
      EUNISb_1 == "T" ~ "Forests and other wooded land",
      EUNISb_1 == "S" ~ "Heathlands, scrub and tundra",
      EUNISb_1 == "R" ~ "Grasslands",
      EUNISb_1 == "Q" ~ "Wetlands",
      EUNISb_1 == "P" ~ "Inland waters",
      EUNISb_1 == "N" ~ "Coastal habitats",
      EUNISb_1 == "MA" ~ "Marine habitats",
      TRUE ~ NA_character_
    ),
    EUNISc_1_descr = case_when(
      EUNISc_1 == "V" ~ "Vegetated man-made habitats",
      EUNISc_1 == "U" ~ "Inland habitats with no or little soil",
      EUNISc_1 == "T" ~ "Forests and other wooded land",
      EUNISc_1 == "S" ~ "Heathlands, scrub and tundra",
      EUNISc_1 == "R" ~ "Grasslands",
      EUNISc_1 == "Q" ~ "Wetlands",
      EUNISc_1 == "P" ~ "Inland waters",
      EUNISc_1 == "N" ~ "Coastal habitats",
      EUNISc_1 == "MA" ~ "Marine habitats",
      TRUE ~ NA_character_
    ),
    EUNISd_1_descr = case_when(
      EUNISd_1 == "V" ~ "Vegetated man-made habitats",
      EUNISd_1 == "U" ~ "Inland habitats with no or little soil",
      EUNISd_1 == "T" ~ "Forests and other wooded land",
      EUNISd_1 == "S" ~ "Heathlands, scrub and tundra",
      EUNISd_1 == "R" ~ "Grasslands",
      EUNISd_1 == "Q" ~ "Wetlands",
      EUNISd_1 == "P" ~ "Inland waters",
      EUNISd_1 == "N" ~ "Coastal habitats",
      EUNISd_1 == "MA" ~ "Marine habitats",
      TRUE ~ NA_character_
    )
  )

Plot for EUNISa_1 (the first assigned EUNIS in cases of multiple assignations, level 1):

ggplot(db_EVA, aes(EUNISa_1_descr)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of EVA (without ReSurvey) observations",
       x = "EUNIS level 1") + coord_flip()

ggplot(db_EVA %>% filter(!is.na(EUNISa_1_descr)), aes(EUNISa_1_descr)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of EVA (without ReSurvey) observations",
       x = "EUNIS level 1") + coord_flip()

Check if values of EUNISa_1, EUNISb_1, EUNISc_1 and EUNISd_1 are among the allowed values:

db_EVA %>%
  select(all_of(c("EUNISa_1", "EUNISb_1", "EUNISc_1", "EUNISd_1"))) %>%
  pivot_longer(everything(), names_to = "column", values_to = "value") %>%
  filter(!is.na(value)) %>%
  distinct(column, value) %>%
  mutate(is_valid = value %in% EUNIS1_codes) %>%  
  group_by(column) %>%
  summarise(
    all_valid = all(is_valid),
    invalid_values = list(value[!is_valid]))

All level 1 values are valid.

CHECK IF THERE ARE WRONG EUNIS VALUES FROM LEVEL 2 AND UP

ISSUE 9: Manipulated plots and info on manipulation type

ggplot(db_EVA, aes(`Manipulate (y/n)`)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "Manipulation")

List of Type of Manipulation (mixed information), supposedly no manipulated plots here (!?):

unique(db_EVA$`Type of manipulation`)
[1] NA                 "reference forest" "x"               

ISSUE 10: Location method

ggplot(db_EVA, aes(`Location method`)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of EVA (without ReSurvey) observations",
       x = "Location method") + coord_flip()

ISSUE 11: Resurvey project types

unique(db_EVA$RS_PROJTYP)
[1] NA           "permanent"  "resampling"
ggplot(db_EVA, aes(RS_PROJTYP, fill=`Manipulate (y/n)`)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of EVA (without ReSurvey) observations",
       x = "Resurvey project type") + coord_flip() +
  theme(legend.position = "top")

ISSUE 13: Location uncertainty

db_EVA <- db_EVA %>%
  # Redefine precision_new, which was wrong
  mutate(precision_new = factor(ifelse(is.na(Lon_prec) & is.na(Lat_prec),
                                       0, 1)))
ggplot(db_EVA, aes(`Location uncertainty (m)`, fill = precision_new)) +
  geom_histogram( color = "black") +
  xlab("Location uncertainty (m)")

ggplot(db_EVA %>% filter(`Location uncertainty (m)` <= 500),
       aes(`Location uncertainty (m)`, fill = precision_new)) +
  geom_histogram(color = "black") +
  xlab("Location uncertainty (m) <= 500")

ggplot(db_EVA %>% filter(`Location uncertainty (m)` > 500),
       aes(`Location uncertainty (m)`, fill = precision_new)) +
  geom_histogram(color = "black") +
  xlab("Location uncertainty (m) > 500")

NO ISSUES FROM HERE

Altitude and slope values: REVISE IF USE!

Unique slope values:

unique((db_EVA)$`Slope (°)`) %>% str_sort()
  [1] "-1"  "-2"  "-5"  "-9"  "0"   "0.5" "1"   "10"  "11"  "12"  "13"  "14" 
 [13] "15"  "16"  "17"  "18"  "19"  "2"   "20"  "21"  "22"  "23"  "24"  "25" 
 [25] "26"  "27"  "28"  "29"  "3"   "30"  "31"  "32"  "33"  "34"  "35"  "36" 
 [37] "37"  "38"  "39"  "4"   "40"  "41"  "42"  "43"  "44"  "45"  "46"  "47" 
 [49] "48"  "49"  "5"   "50"  "51"  "52"  "53"  "54"  "55"  "56"  "57"  "58" 
 [61] "59"  "6"   "60"  "61"  "62"  "63"  "64"  "65"  "66"  "67"  "68"  "69" 
 [73] "7"   "70"  "71"  "72"  "73"  "74"  "75"  "76"  "77"  "78"  "79"  "8"  
 [85] "80"  "81"  "82"  "83"  "84"  "85"  "86"  "87"  "88"  "89"  "9"   "90" 
 [97] "92"  "93"  "95"  "97"  "98"  "99"  NA   

Set altitude, slope and aspect as numeric:

db_EVA <- db_EVA %>%
  mutate(
    # Some altitude values have a "-" after the number,
    # convert to numeric after removing that
    Altitude = as.numeric(gsub("-", "", Altitude)),
    # Some slope values are noted as "_" or "-", these should be NA,
    # otherwise convert to numeric
    `Slope (°)` = ifelse(`Slope (°)` == "_" | `Slope (°)` == "-",
                   NA, as.numeric(`Slope (°)`)),
    # Convert aspect values to numeric
    `Aspect (°)` = as.numeric(`Aspect (°)`)
    )
Aviso: There was 1 warning in `mutate()`.
ℹ In argument: `Slope (°) = ifelse(`Slope (°)` == "_" | `Slope (°)` == "-", NA,
  as.numeric(`Slope (°)`))`.
Caused by warning in `ifelse()`:
! NAs introducidos por coerción

Histograms:

ggplot(db_EVA, aes(Altitude)) +
  geom_histogram(fill = "white", color = "black")

ggplot(db_EVA, aes(`Aspect (°)`)) +
  geom_histogram(fill = "white", color = "black")

ggplot(db_EVA, aes(`Slope (°)`)) +
  geom_histogram(fill = "white", color = "black")

range(db_EVA$`Slope (°)`, na.rm=T)
[1] -9 99

Add columns date and year

db_EVA <- db_EVA %>%
  mutate(date = dmy(`Date of recording`), year = year(date))
Aviso: There was 1 warning in `mutate()`.
ℹ In argument: `date = dmy(`Date of recording`)`.
Caused by warning:
!  98 failed to parse.

Histograms:

ggplot(db_EVA, aes(year)) + geom_histogram(fill = "white", color = "black")

Plot size

ggplot(db_EVA, aes(`Relevé area (m²)`)) +
  geom_histogram(fill = "white", color = "black")

Observations with no info on plot size:

nrow(db_EVA %>% filter(is.na(`Relevé area (m²)`)))
[1] 465792

Cover values (total, trees, shrubs, herbs, mossess)

db_EVA %>%
  pivot_longer(cols = c(`Cover total (%)`, `Cover tree layer (%)`,
                        `Cover shrub layer (%)`, `Cover herb layer (%)`,
                        `Cover moss layer (%)`),
               names_to = "variable", values_to = "value") %>%
  ggplot(aes(x = value)) +
  geom_histogram(fill = "white", color = "black", bins = 10) +
  facet_wrap(~ variable, scales = "free") +
  labs(x = "Value", y = "Frequency")

db_EVA %>%
  reframe(across(c(`Cover total (%)`, `Cover tree layer (%)`,
                     `Cover shrub layer (%)`, `Cover herb layer (%)`,
                     `Cover moss layer (%)`), ~range(., na.rm = TRUE)))

All values OK.

Mosses and lichens identified

ggplot(db_EVA, aes(`Mosses identified (Y/N)`)) + geom_bar()

ggplot(db_EVA, aes(`Lichens identified (Y/N)`)) + geom_bar()

NA in most cases.

Info on HabitatID from DK

Based on information got from Jesper.

Read the data sent by Jesper from DK

db_DK_J<-read_tsv(here("data", "raw",
                       "DK_Naturdata_Res_habitat_hab_codes_Jesper",
                  "DK_Naturdata_Res_habitat_hab_codes.txt"))
Rows: 158800 Columns: 9── Column specification ──────────────────────────────────────────────────────────
Delimiter: "\t"
chr (2): HABITAT, Dataset
dbl (7): PlotObservationID, RELEVE_NR, CIRC_ID, PLOT5_ID, PLOT15_ID, Access re...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.

Add info on HabitatID to db_EVA

db_EVA <- db_EVA %>%
  # Keeping all obs in db_EVA but not all in db_DK_J
  left_join(db_DK_J %>% select(PlotObservationID, HabitatID))
Joining with `by = join_by(PlotObservationID)`

List of HabitatID

print(db_EVA %>% distinct(HabitatID), n = 100)

Write csv:

write_csv(db_EVA %>% distinct(HabitatID),
          here("data", "clean","list_HabitatID_DK_EVA.csv"))

Cases without HabitatID info

Cases without ESy EUNIS habitat:

nrow(db_EVA %>% filter(is.na(`Expert System`)))/nrow(db_EVA)
[1] 0.3751393

Cases without ESy EUNIS habitat but with HabitatID from DK:

nrow(db_EVA %>% filter(is.na(`Expert System`)&!is.na(HabitatID)))/nrow(db_EVA)
[1] 0.0001090831

Cases without ESy EUNIS habitat and without HABITAT from DK:

nrow(db_EVA %>% 
       filter(is.na(`Expert System`)&is.na(HabitatID)))/nrow(db_EVA)
[1] 0.3750303

Cases without ESy EUNIS habitat and without HabitatID from DK where data is presence / absence:

nrow(db_EVA %>%
       filter(is.na(`Expert System`) &
                is.na(HabitatID) &
                `Cover abundance scale` == "Presence/Absence"))/
  nrow(db_EVA)
[1] 0.1504649

Cases without ESy EUNIS habitat and without HabitatID from DK where data is not presence / absence:

nrow(db_EVA %>%
       filter(is.na(`Expert System`) &
                is.na(HabitatID) &
                `Cover abundance scale` != "Presence/Absence"))/
  nrow(db_EVA)
[1] 0.2245654

Change some Annex I habitat codes that were wrong

db_EVA <- db_EVA %>%
  mutate(HabitatID = as.character(HabitatID)) %>%
  mutate(HabitatID = ifelse(HabitatID == "9998", "91D0",
                            ifelse(HabitatID == "9999", "91E0", HabitatID)))

Add info on correspondences HabitatID (DK, Jesper) - EUNIS

Read correspondences file:

correspondences<-read_excel(here("data", "edited",
                                 "correspondence_HabitatID_DK.xlsx"))

Add info to db_EVA:

db_EVA <- db_EVA %>%
  # Keeping all obs in db_EVA but not all in db_DK_J
  left_join(correspondences %>% select(HabitatID, EUNIS))
Joining with `by = join_by(HabitatID)`

Correct NA values in EUNIS

db_EVA <- db_EVA %>%
  mutate(EUNIS = ifelse(EUNIS == "NA", NA, EUNIS))

Add info on EUNIS (DK) to EUNISa:

db_EVA <- db_EVA %>%
  mutate(EUNISa =
           # If EUNIS (DK) is available, add as EUNISa
           ifelse(!is.na(EUNIS), EUNIS, 
                  # Otherwise keep EUNISa
                  EUNISa),
         EUNIS_assignation = ifelse(!is.na(EUNIS), "Info from DK",
                                    ifelse(is.na(EUNISa), "Not possible",
                                           "Expert system"))) %>%
  # Remove column EUNIS (DK)
  select(-EUNIS)
ggplot(db_EVA, aes(EUNIS_assignation)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of EVA (not ReSurvey) observations",
       x = "EUNIS assignation")

Update columns for EUNIS levels and descriptions

Update the columns for the different EUNISs levels:

db_EVA <- db_EVA %>%
  mutate(
    # EUNISa levels
    EUNISa_1 = substr(EUNISa, 1, ifelse(str_starts(EUNISa, "MA"), 2, 1)),
    EUNISa_2 = ifelse(
      nchar(EUNISa) >= ifelse(str_starts(EUNISa, "MA"), 3, 2), 
      substr(EUNISa, 1, ifelse(str_starts(EUNISa, "MA"), 3, 2)),
      NA_character_
    ),
    EUNISa_3 = ifelse(
      nchar(EUNISa) >= ifelse(str_starts(EUNISa, "MA"), 4, 3), 
      substr(EUNISa, 1, ifelse(str_starts(EUNISa, "MA"), 4, 3)),
      NA_character_
      ),
    EUNISa_4 = ifelse(
      nchar(EUNISa) >= ifelse(str_starts(EUNISa, "MA"), 5, 4), 
      substr(EUNISa, 1, ifelse(str_starts(EUNISa, "MA"), 5, 4)),
      NA_character_
    )
  ) %>%
  # Remove HabitatID column
  select(-HabitatID)

Update columns with descriptions for the level 1 codes:

db_EVA <- db_EVA %>%
  mutate(
    EUNISa_1_descr = case_when(
      EUNISa_1 == "V" ~ "Vegetated man-made habitats",
      EUNISa_1 == "U" ~ "Inland habitats with no or little soil",
      EUNISa_1 == "T" ~ "Forests and other wooded land",
      EUNISa_1 == "S" ~ "Heathlands, scrub and tundra",
      EUNISa_1 == "R" ~ "Grasslands",
      EUNISa_1 == "Q" ~ "Wetlands",
      EUNISa_1 == "P" ~ "Inland waters",
      EUNISa_1 == "N" ~ "Coastal habitats",
      EUNISa_1 == "MA" ~ "Marine habitats",
      TRUE ~ NA_character_
    )
  )

Number of different EUNIS codes

Recalculate how many different EUNIS codes have been assigned:

db_EVA <- db_EVA %>%
  mutate(
    # Count the number of non-NA values across the EUNIS columns
    n_EUNIS = rowSums(!is.na(select(., EUNISa:EUNISd)))
  )
ggplot(db_EVA, aes(n_EUNIS)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of EVA (not ReSurvey) observations",
       x = "Number of differnt EUNIS codes assigned") + coord_flip()

ggplot(db_EVA %>% filter(n_EUNIS > 0), aes(n_EUNIS)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of EVA (not ReSurvey) observations",
       x = "Number of differnt EUNIS codes assigned") + coord_flip()

New plot for EUNISa_1 (the first assigned EUNIS in cases of multiple assignations, level 1):

ggplot(db_EVA, aes(EUNISa_1_descr)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of EVA (not ReSurvey) observations",
       x = "EUNIS level 1") + coord_flip()

ggplot(db_EVA %>% filter(!is.na(EUNISa_1_descr)), aes(EUNISa_1_descr)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of EVA (not ReSurvey) observations",
       x = "EUNIS level 1") + coord_flip()

Add info on descriptions for EUNIS levels 2-4 - CHECK IF USE!

descriptions<-read_excel(here("data", "edited",
                                 "EUNIS-Habitats-2021-06-01_modified.xlsx"))
# Define the columns and corresponding description column names
eunis_cols <- c("EUNISa_2", "EUNISa_3", "EUNISa_4",
                "EUNISb_2", "EUNISb_3", "EUNISb_4", 
                "EUNISc_2", "EUNISc_3", "EUNISc_4",
                "EUNISd_2", "EUNISd_3", "EUNISd_4")

# Create corresponding description column names
descr_col_names <- paste0(eunis_cols, "_descr")

# Use reduce to loop through the columns and join dynamically based on level
db_EVA <- reduce(seq_along(eunis_cols), function(data, i) {
  # Extract level number from the column name (e.g., EUNISa_2 -> 2)
  level <- as.numeric(gsub("\\D", "", eunis_cols[i]))
  
  # Filter descriptions for the corresponding level
  descriptions_level <- descriptions %>%
    filter(level == level) %>%
    select(`EUNIS 2020 code`, `EUNIS-2021 habitat name`)
  
  # Perform the left_join and rename the column dynamically
  data %>%
    left_join(
      descriptions_level,
      by = setNames("EUNIS 2020 code", eunis_cols[i])
    ) %>%
    rename(!!descr_col_names[i] := `EUNIS-2021 habitat name`)
}, .init = db_EVA)

The matching did not work sometimes, correct!

# Correct EUNISa levels 2-4 descriptions
db_EVA <- db_EVA %>%
  mutate(EUNISa_2_descr = 
           ifelse(!is.na(EUNISa_2_descr), EUNISa_2_descr,
                  case_when(
                    EUNISa_2 == "Pf" ~ "Fresh-water submerged vegetation",
                    EUNISa_2 == "Pj" ~ "Stonewort vegetation",
                    EUNISa_2 == "R4" ~ "Alpine and subalpine grasslands",
                    EUNISa_2 == "Pb" ~ "Calcareous spring and spring brook",
                    EUNISa_2 == "Qb" ~ "Wetlands",
                    EUNISa_2 == "R3" ~ "Seasonally wet and wet grasslands",
                    EUNISa_2 == "Qa" ~ "Mires",
                    EUNISa_2 == "Pa" ~ "Base-poor spring and spring brook",
                    EUNISa_2 == "Ph" ~ "Oligotrophic-water vegetation",
                    EUNISa_2 == "Pg" ~ "Fresh-water nymphaeid vegetation",
                    EUNISa_2 ==
                      "Pd" ~ "Fresh-water small pleustophyte vegetation",
                    EUNISa_2 == "Pc" ~ "Brackish-water vegetation",
                    EUNISa_2 ==
                      "Pe" ~ "Fresh-water large pleustophyte vegetation",
                    EUNISa_2 == "Pi" ~ "Dystrophic-water vegetation",
                    EUNISa_2 == "S1" ~ "Tundra",
                    EUNISa_2 ==
                      "U7" ~ "Unvegetated or sparsely vegetated gravel bars",
                    EUNISa_2 == "Q6" ~ "Periodically exposed shores",
                    TRUE ~ NA_character_)
                  ),
         EUNISa_3_descr = 
           ifelse(!is.na(EUNISa_3_descr), EUNISa_3_descr,
                  case_when(
                    EUNISa_3 =="U71" ~ "Unvegetated or sparsely vegetated gravel bar in montane and alpine regions",
                    EUNISa_3 =="Q61" ~ "Periodically exposed shore with stable, eutrophic sediments with pioneer or ephemeral vegetation",
                    EUNISa_3 =="Q62" ~ "Periodically exposed shore with stable, mesotrophic sediments with pioneer or ephemeral vegetation",
                    TRUE ~ NA_character_
                    ))
         )
# Correct EUNISb levels 2-4 descriptions
db_EVA <- db_EVA %>%
  mutate(EUNISb_2_descr = 
           ifelse(!is.na(EUNISb_2_descr), EUNISb_2_descr,
                  case_when(
                    EUNISb_2 == "Pj" ~ "Stonewort vegetation",
                    EUNISb_2 == "R4" ~ "Alpine and subalpine grasslands",
                    EUNISb_2 == "Pf" ~ "Fresh-water submerged vegetation",
                    TRUE ~ NA_character_)
                  )
         )

EUNISc and EUNISd levels 2-4 are OK.

Notes EUNIS codes - to change?

https://www.sci.muni.cz/botany/chytry/Schaminee_etal2021_EEA-Report-Aquatic-Wetland-habitats.pdf

EUNISa_2 == “Q6” : “Periodically exposed shores” EUNISa_3 = “Q61” : “Periodically exposed shore with stable, eutrophic sediments with pioneer or ephemeral vegetation” EUNISa_3 == “Q62” : “Periodically exposed shore with stable, mesotrophic sediments with pioneer or ephemeral vegetation”

This classification of Q + numbers is now coexisting in the database with Qa & Qb (metadata). How to proceed?

db_EVA %>% filter(EUNISa_1 == "Q") %>% distinct(EUNISa_2)

Plots of level-2 categories within each level 1 category

ggplot(db_EVA %>% filter(EUNISa_1 == "MA"), aes(EUNISa_2_descr)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "EUNIS level 2") + coord_flip() +
  ggtitle(db_EVA %>% filter(EUNISa_1 == "MA") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","MA_level2.tiff"),
       width=14,height=8,units="cm",dpi=300)

ggplot(db_EVA %>% filter(EUNISa_1 == "N"), aes(EUNISa_2_descr)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "EUNIS level 2") + coord_flip() +
  ggtitle(db_EVA %>% filter(EUNISa_1 == "N") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","N_level2.tiff"),
       width=14,height=8,units="cm",dpi=300)

ggplot(db_EVA %>% filter(EUNISa_1 == "P"), aes(EUNISa_2_descr)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "EUNIS level 2") + coord_flip() +
  ggtitle(db_EVA %>% filter(EUNISa_1 == "P") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","P_level2.tiff"),
       width=14,height=8,units="cm",dpi=300)

ggplot(db_EVA %>% filter(EUNISa_1 == "Q"), aes(EUNISa_2_descr)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "EUNIS level 2") + coord_flip() +
  ggtitle(db_EVA %>% filter(EUNISa_1 == "Q") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","Q_level2.tiff"),
       width=14,height=8,units="cm",dpi=300)

ggplot(db_EVA %>% filter(EUNISa_1 == "R"), aes(EUNISa_2_descr)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "EUNIS level 2") + coord_flip() +
  ggtitle(db_EVA %>% filter(EUNISa_1 == "R") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","R_level2.tiff"),
       width=14,height=8,units="cm",dpi=300)

ggplot(db_EVA %>% filter(EUNISa_1 == "S"), aes(EUNISa_2_descr)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "EUNIS level 2") + coord_flip() +
  ggtitle(db_EVA %>% filter(EUNISa_1 == "S") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","S_level2.tiff"),
       width=16,height=8,units="cm",dpi=300)

ggplot(db_EVA %>% filter(EUNISa_1 == "T"), aes(EUNISa_2_descr)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "EUNIS level 2") + coord_flip() +
  ggtitle(db_EVA %>% filter(EUNISa_1 == "T") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","T_level2.tiff"),
       width=14,height=8,units="cm",dpi=300)

ggplot(db_EVA %>% filter(EUNISa_1 == "U"), aes(EUNISa_2_descr)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "EUNIS level 2") + coord_flip() +
  ggtitle(db_EVA %>% filter(EUNISa_1 == "U") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","U_level2.tiff"),
       width=16,height=8,units="cm",dpi=300)

ggplot(db_EVA %>% filter(EUNISa_1 == "V"), aes(EUNISa_2_descr)) +
         geom_bar(aes(y = (..count..) / sum(..count..) * 100)) +
  labs(y = "Percentage of ReSurvey observations",
       x = "EUNIS level 2") + coord_flip() +
  ggtitle(db_EVA %>% filter(EUNISa_1 == "V") %>% distinct(EUNISa_1_descr))
ggsave(filename=here("output", "figures","V_level2.tiff"),
       width=14,height=8,units="cm",dpi=300)

Save to clean data

Save so-far clean datafile for EVA database (not resurveys):

write_tsv(db_EVA,here("data", "clean","db_EVA_clean.csv"))

Session info

sessionInfo()
LS0tDQp0aXRsZTogIlNjcmlwdCB0byBtYWtlIGEgZmlyc3QgY2hlY2sgb2YgdGhlIEVWQSBkYXRhYmFzZSAobm90IHJlc3VydmV5cykgZm9yIE1PVElWQVRFIg0Kb3V0cHV0Og0KICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KLS0tDQoNCiMgTG9hZCBsaWJyYXJpZXMNCg0KYGBge3J9DQpsaWJyYXJ5KGhlcmUpDQpsaWJyYXJ5KHJlYWRyKQ0KbGlicmFyeSh0aWR5cikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoc3RyaW5ncikNCmxpYnJhcnkocHVycnIpDQpsaWJyYXJ5KHJlYWR4bCkNCmBgYA0KDQojIFJlYWQgdGhlIGRhdGENCg0KYGBge3J9DQpkYl9FVkE8LXJlYWRfdHN2KGhlcmUoImRhdGEiLCAiZWRpdGVkIiwgImRiX0VWQS5jc3YiKSkNCmBgYA0KDQojIFByb2JsZW1zIChkbyBub3QgYWZmZWN0IHVzKQ0KDQpgYGB7cn0NCnByb2JsZW1zPC1wcm9ibGVtcyhkYl9FVkEpDQpzb3J0KHVuaXF1ZShwcm9ibGVtcyRjb2wpKQ0KbmFtZXMoZGJfRVZBW2MoMTMsIDQyKV0pDQpgYGANCg0KIyBVcGRhdGUgY29vcmRpbmF0ZXMNCg0KQ3JlYXRlIG5ldyBjb2x1bW4gd2l0aCBvbGQgY29vcmRpbmF0ZXMgaWYgbmV3IG5vdCBhdmFpbGFibGUsIGFuZCB3aXRoIG5ldyBpZiBhdmFpbGFibGUuDQoNCmBgYHtyfQ0KZGJfRVZBIDwtIGRiX0VWQSAlPiUNCiAgIyBMb25fcHJlYyB5IExhdF9wcmVjIGFsd2F5cyBOQSENCiAgbXV0YXRlKExvbl91cGRhdGVkID0gaWZlbHNlKGlzLm5hKExvbl9wcmVjKSxMb25naXR1ZGUsTG9uX3ByZWMpLA0KICAgICAgICAgTGF0X3VwZGF0ZWQgPSBpZmVsc2UoaXMubmEoTGF0X3ByZWMpLExhdGl0dWRlLExhdF9wcmVjKSkNCmBgYA0KDQpgYGB7cn0NCnByaW50KGRiX0VWQSwgd2lkdGggPSBJbmYpDQpgYGANCg0KIyBJU1NVRSAyOiBDb29yZGluYXRlcyBhcmUgTkENCg0KQ29vcmRpbmF0ZXMgYXJlIGFsc28gTkEgaW4gc29tZSBjYXNlcy4NCg0KYGBge3J9DQpucm93KGRiX0VWQSU+JWZpbHRlcihpcy5uYShMb25fdXBkYXRlZCkgJiBpcy5uYShMYXRfdXBkYXRlZCApKSkNCmBgYA0KDQojIE51bWJlciBvZiBvYnNlcnZhdGlvbnMNCg0KTnVtYmVyIG9mIG9ic2VydmF0aW9ucyBmb3IgZWFjaCBwbG90IHNob3VsZCBiZSAxLCBhcyB0aGlzIGlzIEVWQSAod2l0aG91dCBSZVN1cnZleSkuDQpJIGFtIG5vdCBzdXJlIGhvdyB0byB1bmlxdWVseSBpZGVudGlmeSBlYWNoIHBsb3QgaGVyZSENCg0KIyBTb21lIGRhdGVzIHJlY29yZGVkIGFzIDA6MDA6MDA6IHNldCB0byBOQQ0KDQojIElTU1VFIDU6IERhdGFzZXRzIHdpdGggb25seSBwcmVzZW5jZS9hYnNlbmNlDQoNCmBgYHtyfQ0KZGJfRVZBICU+JQ0KICBmaWx0ZXIoYENvdmVyIGFidW5kYW5jZSBzY2FsZWA9PSJQcmVzZW5jZS9BYnNlbmNlIikgJT4lDQogIGRpc3RpbmN0KERhdGFzZXQpDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGJfRVZBICU+JSANCiAgICAgICAgIG11dGF0ZShwcmVzX29yX2FiID1pZmVsc2UoYENvdmVyIGFidW5kYW5jZSBzY2FsZWA9PSJQcmVzZW5jZS9BYnNlbmNlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlByZXNlbmNlL0Fic2VuY2UiLCAiQWJ1bmRhbmNlIiksDQogICAgICAgICAgICAgICAgREtfTmF0dXJkYXRhX1JlcyA9IGlmZWxzZShEYXRhc2V0ID09ICJES19OYXR1cmRhdGFfUmVzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJZIiwgIk4iKSksDQogICAgICAgICAgICAgICAgYWVzKHByZXNfb3JfYWIsIGZpbGwgPSBES19OYXR1cmRhdGFfUmVzKSkgKw0KICAgICAgICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pICogMTAwKSkgKw0KICBsYWJzKHkgPSAiUGVyY2VudGFnZSIsIHggPSBOVUxMKQ0KIyBWZXJ5IHNtYWxsICUgb2YgREtfTmF0dXJkYXRhX1JlcyA9PSAiWSIgaW4gUHJlc2VuY2UvQWJzZW5jZSANCiMgYnV0IG5vdCB2aXNpYmxlIGluIHRoZSBmaWd1cmUNCmBgYA0KDQpGb3IgREtfTmF0dXJkYXRhX1JlcyAtIGluZm8gYWJvdXQgaGFiaXRhdCBmcm9tIEplcmtlcidzIGZpbGUgKHNlZSBiZWxvdykuDQoNCiMgSVNTVUUgNjogT2JzZXJ2YXRpb25zIHdpdGggd3JvbmcgY291bnRyeSAoVEJEIGluIEdJUykNCg0KIyBJU1NVRSA3OiBEaWZmZXJlbnQgY292ZXIgYWJ1bmRhbmNlIHNjYWxlcw0KDQpgYGB7cn0NCmdncGxvdChkYl9FVkEsIGFlcyhgQ292ZXIgYWJ1bmRhbmNlIHNjYWxlYCkpICsNCiAgICAgICAgIGdlb21fYmFyKGFlcyh5ID0gKC4uY291bnQuLikgLyBzdW0oLi5jb3VudC4uKSAqIDEwMCkpICsNCiAgbGFicyh5ID0gIlBlcmNlbnRhZ2Ugb2YgRVZBICh3aXRob3V0IFJlU3VydmV5KSBvYnNlcnZhdGlvbnMiLA0KICAgICAgIHggPSAiQ292ZXIgYWJ1bmRhbmNlIHNjYWxlIikgKw0KICBjb29yZF9mbGlwKCkNCmBgYA0KDQpTdXBwb3NpbmcgYWxsIGFidW5kYW5jZSBzY2FsZXMgYXJlIGVxdWl2YWxlbnQuDQoNCiMgRVVOSVMgY29kZXMNCg0KVXNlZCB0aGlzIGluZm8gaW4gbWV0YWRhdGEgZmlsZToNCg0KRXhwZXJ0IHN5c3RlbSBjbGFzc2lmaWNhdGlvbiB0byBFVU5JUyBoYWJpdGF0cyAoaHR0cHM6Ly96ZW5vZG8ub3JnL3JlY29yZHMvNDgxMjczNiA7IGh0dHBzOi8vZmxvcmF2ZWcuZXUvaGFiaXRhdC8pLiANCkkgYW0gc2VuZGluZyB5b3UgbGVnZW5kIGZvciBFVU5JUyBjbGFzc2lmaWNhdGlvbiB2ZXJzaW9uIDIwMjItMTAtMTYgd2l0aCBhbGwgY29kZXMgYW5kIG1lYW5pbmdzLCBkaXJlY3RseSBwcmVwYXJlZCBmcm9tIGV4cGVydCBzeXN0ZW0gZmlsZSAoc2Vjb25kIHNoZWV0KSAtIGl0IGlzIHNsaWdodGx5IGRpZmZlcmVudCBmcm9tIHB1Ymxpc2hlZCB2ZXJzaW9uIGluIFpFTk9ETyAoaHR0cHM6Ly96ZW5vZG8ub3JnL3JlY29yZHMvNDgxMjczNiAsIGxpdHRsZSBiaXQgb2xkIGRhdGVkIG5vdykgYW5kIGZyb20gaHR0cHM6Ly9mbG9yYXZlZy5ldS9oYWJpdGF0LyAobGl0dGxlIGJpdCBuZXdlciB0aGFuIGluIGN1cnJlbnQgRVZBIHZlcnNpb24pLg0KDQpRYSA9IG1pcmVzIGFuZCBRYiA9IHdldGxhbmRzDQpQIHVuaXRzIOKAkyBpbiBmbG9yYXZlZy5ldSB0aGVyZSBpcyBzbGlnaHRseSBkaWZmZXJlbnQgY2xhc3NpZmljYXRpb24gKGh0dHBzOi8vZmxvcmF2ZWcuZXUvaGFiaXRhdC9vdmVydmlldy9QKSwgYnV0IGluIEVWQSBpcyBzdGlsbCB0aGlzIGNsYXNzaWZpY2F0aW9uIG9mIFA6DQoNClAgU3VyZmFjZSB3YXRlcnMNClBhIEJhc2UtcG9vciBzcHJpbmcgYW5kIHNwcmluZyBicm9vaw0KUGIgQ2FsY2FyZW91cyBzcHJpbmcgYW5kIHNwcmluZyBicm9vaw0KUGMgQnJhY2tpc2gtd2F0ZXIgdmVnZXRhdGlvbg0KUGQgRnJlc2gtd2F0ZXIgc21hbGwgcGxldXN0b3BoeXRlIHZlZ2V0YXRpb24NClBlIEZyZXNoLXdhdGVyIGxhcmdlIHBsZXVzdG9waHl0ZSB2ZWdldGF0aW9uDQpQZiBGcmVzaC13YXRlciBzdWJtZXJnZWQgdmVnZXRhdGlvbg0KUGcgRnJlc2gtd2F0ZXIgbnltcGhhZWlkIHZlZ2V0YXRpb24NClBoIE9saWdvdHJvcGhpYy13YXRlciB2ZWdldGF0aW9uDQpQaSBEeXN0cm9waGljLXdhdGVyIHZlZ2V0YXRpb24NClBqIFN0b25ld29ydCB2ZWdldGF0aW9uIA0KDQpQcmVzZW5jZSBvZiDigJwh4oCdIHNpbXBseSBtZWFucyB0aGF0IGZvciBvbmUgdW5pdCB0aGVyZSBhcmUgdHdvIG9yIG1vcmUgZGlmZmVyZW50IGZvcm11bGFzLCBlLmcuIFIxMSBhbmQgUjExIS4gU28gaXQgaXMgb25seSB0ZWNobmljYWwgc3R1ZmYuDQoNCk11bHRpcGxlIGFzc2lnbm1lbnQgb2YgcmVsZXbDqSDigJMgbm8gcHJpb3JpdHksIGFscGhhYmV0aWNhbCBvcmRlciwgZS5nLiBOMTYhLFM2NixTODEgbWVhbnMgdGhhdCByZWxldsOpIGNhbiBiZSBhc3NpZ25lZCB0byBhbGwgMyB1bml0czogTjE2IE1lZGl0ZXJyYW5lYW4gYW5kIE1hY2Fyb25lc2lhbiBjb2FzdGFsIGR1bmUgZ3Jhc3NsYW5kIChncmV5IGR1bmUpLCBTNjYgTWVkaXRlcnJhbmVhbiBoYWxvLW5pdHJvcGhpbG91cyBzY3J1YiBhbmQgUzgxIENhbmFyaWFuIHhlcm9waHl0aWMgc2NydWINCg0KTm8gdmFsdWUgcHJlc2VudCBpbiBFeHBlcnQgU3lzdGVtIOKAkyByZWxldsOpIGRpZG7CtHQgZW50ZXIgZXhwZXJ0IHN5c3RlbSBjbGFzc2lmaWNhdGlvbiAoPSBpdCBtZWFucyB0aGF0IHNvbWUgcHJlcmVxdWlzaXRlcyBhcmUgbWlzc2luZykNCg0K4oCcfuKAnSDigJMgcmVsZXbDqSBlbnRlcmVkIGV4cGVydCBjbGFzc2lmaWNhdGlvbiBob3dldmVyIHdhcyBub3QgY2xhc3NpZmllZCB0byBhbnkgRVVOSVMgdW5pdA0KKw0KDQpDbGVhbiBpbmZvIG9uIEV4cGVydCBzeXN0ZW0gY29sdW1uIGFuZCBzZXBhcmF0ZSBpdCB3aGVuIHRoZXJlIGFyZSBzZXZlcmFsIGNvZGVzLg0KDQpgYGB7cn0NCmRiX0VWQSA8LSBkYl9FVkEgJT4lDQogIG11dGF0ZSgNCiAgICAjIENsZWFuICdFeHBlcnQgU3lzdGVtJyBjb2x1bW4gYnkgcmVtb3ZpbmcgIiEiIGFuZCByZXBsYWNpbmcgIn4iIHdpdGggTkENCiAgICBgRXhwZXJ0IFN5c3RlbWAgPSBjYXNlX3doZW4oDQogICAgICBgRXhwZXJ0IFN5c3RlbWAgPT0gIn4iIH4gTkFfY2hhcmFjdGVyXywgICMgUmVwbGFjZSAifiIgd2l0aCBOQQ0KICAgICAgVFJVRSB+IHN0cl9yZXBsYWNlX2FsbChgRXhwZXJ0IFN5c3RlbWAsICIhIiwgIiIpICAjIFJlbW92ZSAiISINCiAgICApDQogICkgJT4lDQogICMgU2VwYXJhdGUgdGhlIHZhbHVlcyBpbiAnRXhwZXJ0IFN5c3RlbScgaW50byBtdWx0aXBsZSBjb2x1bW5zDQogIHNlcGFyYXRlKA0KICAgIGBFeHBlcnQgU3lzdGVtYCwNCiAgICBpbnRvID0gYygiRVVOSVNhIiwgIkVVTklTYiIsICJFVU5JU2MiLCAiRVVOSVNkIiksDQogICAgc2VwID0gIiwiLA0KICAgIGV4dHJhID0gImRyb3AiLCAgIyBEcm9wIGV4dHJhIHZhbHVlcyBpZiB0aGVyZSBhcmUgbW9yZSB0aGFuIGNvbHVtbnMNCiAgICBmaWxsID0gInJpZ2h0IiwgICAjIEZpbGwgbWlzc2luZyB2YWx1ZXMgd2l0aCBOQSBmb3IgY2FzZXMgd2l0aCBmZXdlciB2YWx1ZXMNCiAgICByZW1vdmUgPSBGQUxTRSAgICAjIEtlZXAgdGhlIG9yaWdpbmFsICdFeHBlcnQgU3lzdGVtJyBjb2x1bW4NCiAgKQ0KYGBgDQoNCkNhbGN1bGF0ZSBob3cgbWFueSBkaWZmZXJlbnQgRVVOSVMgY29kZXMgaGF2ZSBiZWVuIGFzc2lnbmVkOg0KDQpgYGB7cn0NCmRiX0VWQSA8LSBkYl9FVkEgJT4lDQogIG11dGF0ZSgNCiAgICAjIENvdW50IHRoZSBudW1iZXIgb2Ygbm9uLU5BIHZhbHVlcyBhY3Jvc3MgdGhlIEVVTklTIGNvbHVtbnMNCiAgICBuX0VVTklTID0gcm93U3VtcyghaXMubmEoc2VsZWN0KC4sIHN0YXJ0c193aXRoKCJFVU5JUyIpKSkpDQogICkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChkYl9FVkEsIGFlcyhuX0VVTklTKSkgKw0KICAgICAgICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pICogMTAwKSkgKw0KICBsYWJzKHkgPSAiUGVyY2VudGFnZSBvZiBFVkEgKHdpdGhvdXQgUmVTdXJ2ZXkpIG9ic2VydmF0aW9ucyIsDQogICAgICAgeCA9ICJOdW1iZXIgb2YgZGlmZmVybnQgRVVOSVMgY29kZXMgYXNzaWduZWQiKSArIGNvb3JkX2ZsaXAoKQ0KZ2dwbG90KGRiX0VWQSAlPiUgZmlsdGVyKG5fRVVOSVMgPiAwKSwgYWVzKG5fRVVOSVMpKSArDQogICAgICAgICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pIC8gc3VtKC4uY291bnQuLikgKiAxMDApKSArDQogIGxhYnMoeSA9ICJQZXJjZW50YWdlIG9mIEVWQSAod2l0aG91dCBSZVN1cnZleSkgb2JzZXJ2YXRpb25zIiwNCiAgICAgICB4ID0gIk51bWJlciBvZiBkaWZmZXJudCBFVU5JUyBjb2RlcyBhc3NpZ25lZCIpICsgY29vcmRfZmxpcCgpDQpgYGANCg0KQ29ycmVjdCBzb21lIEVVTklTIGNvZGVzIHRoYXQgYXJlIHByb2JhYmx5IHdyb25nIChjb3BpZWQgZnJvbSBSZVN1cnZleSBub3RlYm9vayk6DQoNCmBgYHtyfQ0KZGJfRVZBIDwtIGRiX0VWQSAlPiUNCiAgbXV0YXRlKGFjcm9zcyhzdGFydHNfd2l0aCgiRVVOSVMiKSwgfiBjYXNlX3doZW4oDQogICAgLiA9PSAiTjE2TSIgfiAiTjE2IiwNCiAgICAuID09ICJTYSIgfiAiVjQiLA0KICAgIC4gPT0gIlNiIiB+ICJWNSIsDQogICAgLiA9PSAiVDFDVCIgfiAiVDFDIiwNCiAgICAuID09ICJOMTVBIiB+ICJOMTUiLA0KICAgIFRSVUUgfiAuDQogICkpKQ0KYGBgDQoNCkFkZCBjb2x1bW5zIGZvciB0aGUgZGlmZmVyZW50IEVVTklTIGxldmVscw0KDQpgYGB7cn0NCmRiX0VWQSA8LSBkYl9FVkEgJT4lDQogIG11dGF0ZSgNCiAgICAjIEVVTklTYSBsZXZlbHMNCiAgICBFVU5JU2FfMSA9IHN1YnN0cihFVU5JU2EsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDIsIDEpKSwNCiAgICBFVU5JU2FfMiA9IGlmZWxzZSgNCiAgICAgIG5jaGFyKEVVTklTYSkgPj0gaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNhLCAiTUEiKSwgMywgMiksIA0KICAgICAgc3Vic3RyKEVVTklTYSwgMSwgaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNhLCAiTUEiKSwgMywgMikpLA0KICAgICAgTkFfY2hhcmFjdGVyXw0KICAgICksDQogICAgRVVOSVNhXzMgPSBpZmVsc2UoDQogICAgICBuY2hhcihFVU5JU2EpID49IGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDQsIDMpLCANCiAgICAgIHN1YnN0cihFVU5JU2EsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDQsIDMpKSwNCiAgICAgIE5BX2NoYXJhY3Rlcl8NCiAgICAgICksDQogICAgRVVOSVNhXzQgPSBpZmVsc2UoDQogICAgICBuY2hhcihFVU5JU2EpID49IGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDUsIDQpLCANCiAgICAgIHN1YnN0cihFVU5JU2EsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDUsIDQpKSwNCiAgICAgIE5BX2NoYXJhY3Rlcl8NCiAgICApLA0KICAgIA0KICAgICMgRVVOSVNiIGxldmVscw0KICAgIEVVTklTYl8xID0gc3Vic3RyKEVVTklTYiwgMSwgaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNiLCAiTUEiKSwgMiwgMSkpLA0KICAgIEVVTklTYl8yID0gaWZlbHNlKA0KICAgICAgbmNoYXIoRVVOSVNiKSA+PSBpZmVsc2Uoc3RyX3N0YXJ0cyhFVU5JU2IsICJNQSIpLCAzLCAyKSwgDQogICAgICBzdWJzdHIoRVVOSVNiLCAxLCBpZmVsc2Uoc3RyX3N0YXJ0cyhFVU5JU2IsICJNQSIpLCAzLCAyKSksDQogICAgICBOQV9jaGFyYWN0ZXJfDQogICAgKSwNCiAgICBFVU5JU2JfMyA9IGlmZWxzZSgNCiAgICAgIG5jaGFyKEVVTklTYikgPj0gaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNiLCAiTUEiKSwgNCwgMyksIA0KICAgICAgc3Vic3RyKEVVTklTYiwgMSwgaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNiLCAiTUEiKSwgNCwgMykpLA0KICAgICAgTkFfY2hhcmFjdGVyXw0KICAgICksDQogICAgRVVOSVNiXzQgPSBpZmVsc2UoDQogICAgICBuY2hhcihFVU5JU2IpID49IGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYiwgIk1BIiksIDUsIDQpLCANCiAgICAgIHN1YnN0cihFVU5JU2IsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYiwgIk1BIiksIDUsIDQpKSwNCiAgICAgIE5BX2NoYXJhY3Rlcl8NCiAgICApLA0KICAgIA0KICAgICMgRVVOSVNjIGxldmVscw0KICAgIEVVTklTY18xID0gc3Vic3RyKEVVTklTYywgMSwgaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNjLCAiTUEiKSwgMiwgMSkpLA0KICAgIEVVTklTY18yID0gaWZlbHNlKA0KICAgICAgbmNoYXIoRVVOSVNjKSA+PSBpZmVsc2Uoc3RyX3N0YXJ0cyhFVU5JU2MsICJNQSIpLCAzLCAyKSwgDQogICAgICBzdWJzdHIoRVVOSVNjLCAxLCBpZmVsc2Uoc3RyX3N0YXJ0cyhFVU5JU2MsICJNQSIpLCAzLCAyKSksDQogICAgICBOQV9jaGFyYWN0ZXJfDQogICAgKSwNCiAgICBFVU5JU2NfMyA9IGlmZWxzZSgNCiAgICAgIG5jaGFyKEVVTklTYykgPj0gaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNjLCAiTUEiKSwgNCwgMyksIA0KICAgICAgc3Vic3RyKEVVTklTYywgMSwgaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNjLCAiTUEiKSwgNCwgMykpLA0KICAgICAgTkFfY2hhcmFjdGVyXw0KICAgICksDQogICAgRVVOSVNjXzQgPSBpZmVsc2UoDQogICAgICBuY2hhcihFVU5JU2MpID49IGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYywgIk1BIiksIDUsIDQpLCANCiAgICAgIHN1YnN0cihFVU5JU2MsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYywgIk1BIiksIDUsIDQpKSwNCiAgICAgIE5BX2NoYXJhY3Rlcl8NCiAgICApLA0KICAgIA0KICAgICMgRVVOSVNkIGxldmVscw0KICAgIEVVTklTZF8xID0gc3Vic3RyKEVVTklTZCwgMSwgaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNjLCAiTUEiKSwgMiwgMSkpLA0KICAgIEVVTklTZF8yID0gaWZlbHNlKA0KICAgICAgbmNoYXIoRVVOSVNkKSA+PSBpZmVsc2Uoc3RyX3N0YXJ0cyhFVU5JU2QsICJNQSIpLCAzLCAyKSwgDQogICAgICBzdWJzdHIoRVVOSVNkLCAxLCBpZmVsc2Uoc3RyX3N0YXJ0cyhFVU5JU2QsICJNQSIpLCAzLCAyKSksDQogICAgICBOQV9jaGFyYWN0ZXJfDQogICAgKSwNCiAgICBFVU5JU2RfMyA9IGlmZWxzZSgNCiAgICAgIG5jaGFyKEVVTklTZCkgPj0gaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNkLCAiTUEiKSwgNCwgMyksIA0KICAgICAgc3Vic3RyKEVVTklTZCwgMSwgaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNkLCAiTUEiKSwgNCwgMykpLA0KICAgICAgTkFfY2hhcmFjdGVyXw0KICAgICksDQogICAgRVVOSVNkXzQgPSBpZmVsc2UoDQogICAgICBuY2hhcihFVU5JU2QpID49IGlmZWxzZShzdHJfc3RhcnRzKEVVTklTZCwgIk1BIiksIDUsIDQpLCANCiAgICAgIHN1YnN0cihFVU5JU2QsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTZCwgIk1BIiksIDUsIDQpKSwNCiAgICAgIE5BX2NoYXJhY3Rlcl8NCiAgICApDQogICkNCmBgYA0KDQpBZGQgZGVzY3JpcHRpb25zIGZvciBsZXZlbCAxDQoNCmBgYHtyfQ0KZGJfRVZBIDwtIGRiX0VWQSAlPiUNCiAgbXV0YXRlKA0KICAgIEVVTklTYV8xX2Rlc2NyID0gY2FzZV93aGVuKA0KICAgICAgRVVOSVNhXzEgPT0gIlYiIH4gIlZlZ2V0YXRlZCBtYW4tbWFkZSBoYWJpdGF0cyIsDQogICAgICBFVU5JU2FfMSA9PSAiVSIgfiAiSW5sYW5kIGhhYml0YXRzIHdpdGggbm8gb3IgbGl0dGxlIHNvaWwiLA0KICAgICAgRVVOSVNhXzEgPT0gIlQiIH4gIkZvcmVzdHMgYW5kIG90aGVyIHdvb2RlZCBsYW5kIiwNCiAgICAgIEVVTklTYV8xID09ICJTIiB+ICJIZWF0aGxhbmRzLCBzY3J1YiBhbmQgdHVuZHJhIiwNCiAgICAgIEVVTklTYV8xID09ICJSIiB+ICJHcmFzc2xhbmRzIiwNCiAgICAgIEVVTklTYV8xID09ICJRIiB+ICJXZXRsYW5kcyIsDQogICAgICBFVU5JU2FfMSA9PSAiUCIgfiAiSW5sYW5kIHdhdGVycyIsDQogICAgICBFVU5JU2FfMSA9PSAiTiIgfiAiQ29hc3RhbCBoYWJpdGF0cyIsDQogICAgICBFVU5JU2FfMSA9PSAiTUEiIH4gIk1hcmluZSBoYWJpdGF0cyIsDQogICAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXw0KICAgICksDQogICAgRVVOSVNiXzFfZGVzY3IgPSBjYXNlX3doZW4oDQogICAgICBFVU5JU2JfMSA9PSAiViIgfiAiVmVnZXRhdGVkIG1hbi1tYWRlIGhhYml0YXRzIiwNCiAgICAgIEVVTklTYl8xID09ICJVIiB+ICJJbmxhbmQgaGFiaXRhdHMgd2l0aCBubyBvciBsaXR0bGUgc29pbCIsDQogICAgICBFVU5JU2JfMSA9PSAiVCIgfiAiRm9yZXN0cyBhbmQgb3RoZXIgd29vZGVkIGxhbmQiLA0KICAgICAgRVVOSVNiXzEgPT0gIlMiIH4gIkhlYXRobGFuZHMsIHNjcnViIGFuZCB0dW5kcmEiLA0KICAgICAgRVVOSVNiXzEgPT0gIlIiIH4gIkdyYXNzbGFuZHMiLA0KICAgICAgRVVOSVNiXzEgPT0gIlEiIH4gIldldGxhbmRzIiwNCiAgICAgIEVVTklTYl8xID09ICJQIiB+ICJJbmxhbmQgd2F0ZXJzIiwNCiAgICAgIEVVTklTYl8xID09ICJOIiB+ICJDb2FzdGFsIGhhYml0YXRzIiwNCiAgICAgIEVVTklTYl8xID09ICJNQSIgfiAiTWFyaW5lIGhhYml0YXRzIiwNCiAgICAgIFRSVUUgfiBOQV9jaGFyYWN0ZXJfDQogICAgKSwNCiAgICBFVU5JU2NfMV9kZXNjciA9IGNhc2Vfd2hlbigNCiAgICAgIEVVTklTY18xID09ICJWIiB+ICJWZWdldGF0ZWQgbWFuLW1hZGUgaGFiaXRhdHMiLA0KICAgICAgRVVOSVNjXzEgPT0gIlUiIH4gIklubGFuZCBoYWJpdGF0cyB3aXRoIG5vIG9yIGxpdHRsZSBzb2lsIiwNCiAgICAgIEVVTklTY18xID09ICJUIiB+ICJGb3Jlc3RzIGFuZCBvdGhlciB3b29kZWQgbGFuZCIsDQogICAgICBFVU5JU2NfMSA9PSAiUyIgfiAiSGVhdGhsYW5kcywgc2NydWIgYW5kIHR1bmRyYSIsDQogICAgICBFVU5JU2NfMSA9PSAiUiIgfiAiR3Jhc3NsYW5kcyIsDQogICAgICBFVU5JU2NfMSA9PSAiUSIgfiAiV2V0bGFuZHMiLA0KICAgICAgRVVOSVNjXzEgPT0gIlAiIH4gIklubGFuZCB3YXRlcnMiLA0KICAgICAgRVVOSVNjXzEgPT0gIk4iIH4gIkNvYXN0YWwgaGFiaXRhdHMiLA0KICAgICAgRVVOSVNjXzEgPT0gIk1BIiB+ICJNYXJpbmUgaGFiaXRhdHMiLA0KICAgICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8NCiAgICApLA0KICAgIEVVTklTZF8xX2Rlc2NyID0gY2FzZV93aGVuKA0KICAgICAgRVVOSVNkXzEgPT0gIlYiIH4gIlZlZ2V0YXRlZCBtYW4tbWFkZSBoYWJpdGF0cyIsDQogICAgICBFVU5JU2RfMSA9PSAiVSIgfiAiSW5sYW5kIGhhYml0YXRzIHdpdGggbm8gb3IgbGl0dGxlIHNvaWwiLA0KICAgICAgRVVOSVNkXzEgPT0gIlQiIH4gIkZvcmVzdHMgYW5kIG90aGVyIHdvb2RlZCBsYW5kIiwNCiAgICAgIEVVTklTZF8xID09ICJTIiB+ICJIZWF0aGxhbmRzLCBzY3J1YiBhbmQgdHVuZHJhIiwNCiAgICAgIEVVTklTZF8xID09ICJSIiB+ICJHcmFzc2xhbmRzIiwNCiAgICAgIEVVTklTZF8xID09ICJRIiB+ICJXZXRsYW5kcyIsDQogICAgICBFVU5JU2RfMSA9PSAiUCIgfiAiSW5sYW5kIHdhdGVycyIsDQogICAgICBFVU5JU2RfMSA9PSAiTiIgfiAiQ29hc3RhbCBoYWJpdGF0cyIsDQogICAgICBFVU5JU2RfMSA9PSAiTUEiIH4gIk1hcmluZSBoYWJpdGF0cyIsDQogICAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXw0KICAgICkNCiAgKQ0KYGBgDQoNClBsb3QgZm9yIEVVTklTYV8xICh0aGUgZmlyc3QgYXNzaWduZWQgRVVOSVMgaW4gY2FzZXMgb2YgbXVsdGlwbGUgYXNzaWduYXRpb25zLCBsZXZlbCAxKToNCg0KYGBge3J9DQpnZ3Bsb3QoZGJfRVZBLCBhZXMoRVVOSVNhXzFfZGVzY3IpKSArDQogICAgICAgICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pIC8gc3VtKC4uY291bnQuLikgKiAxMDApKSArDQogIGxhYnMoeSA9ICJQZXJjZW50YWdlIG9mIEVWQSAod2l0aG91dCBSZVN1cnZleSkgb2JzZXJ2YXRpb25zIiwNCiAgICAgICB4ID0gIkVVTklTIGxldmVsIDEiKSArIGNvb3JkX2ZsaXAoKQ0KZ2dwbG90KGRiX0VWQSAlPiUgZmlsdGVyKCFpcy5uYShFVU5JU2FfMV9kZXNjcikpLCBhZXMoRVVOSVNhXzFfZGVzY3IpKSArDQogICAgICAgICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pIC8gc3VtKC4uY291bnQuLikgKiAxMDApKSArDQogIGxhYnMoeSA9ICJQZXJjZW50YWdlIG9mIEVWQSAod2l0aG91dCBSZVN1cnZleSkgb2JzZXJ2YXRpb25zIiwNCiAgICAgICB4ID0gIkVVTklTIGxldmVsIDEiKSArIGNvb3JkX2ZsaXAoKQ0KYGBgDQoNCkNoZWNrIGlmIHZhbHVlcyBvZiBFVU5JU2FfMSwgRVVOSVNiXzEsIEVVTklTY18xIGFuZCBFVU5JU2RfMSBhcmUgYW1vbmcgdGhlIGFsbG93ZWQgdmFsdWVzOg0KDQpgYGB7cn0NCkVVTklTMV9jb2RlcyA8LSBjKCJWIiwgIlUiLCAiVCIsICJTIiwgIlIiLCAiUSIsICJQIiwgIk4iLCAiTUEiKQ0KYGBgDQoNCmBgYHtyfQ0KZGJfRVZBICU+JQ0KICBzZWxlY3QoYWxsX29mKGMoIkVVTklTYV8xIiwgIkVVTklTYl8xIiwgIkVVTklTY18xIiwgIkVVTklTZF8xIikpKSAlPiUNCiAgcGl2b3RfbG9uZ2VyKGV2ZXJ5dGhpbmcoKSwgbmFtZXNfdG8gPSAiY29sdW1uIiwgdmFsdWVzX3RvID0gInZhbHVlIikgJT4lDQogIGZpbHRlcighaXMubmEodmFsdWUpKSAlPiUNCiAgZGlzdGluY3QoY29sdW1uLCB2YWx1ZSkgJT4lDQogIG11dGF0ZShpc192YWxpZCA9IHZhbHVlICVpbiUgRVVOSVMxX2NvZGVzKSAlPiUgIA0KICBncm91cF9ieShjb2x1bW4pICU+JQ0KICBzdW1tYXJpc2UoDQogICAgYWxsX3ZhbGlkID0gYWxsKGlzX3ZhbGlkKSwNCiAgICBpbnZhbGlkX3ZhbHVlcyA9IGxpc3QodmFsdWVbIWlzX3ZhbGlkXSkpDQpgYGANCg0KQWxsIGxldmVsIDEgdmFsdWVzIGFyZSB2YWxpZC4NCg0KIyBDSEVDSyBJRiBUSEVSRSBBUkUgV1JPTkcgRVVOSVMgVkFMVUVTIEZST00gTEVWRUwgMiBBTkQgVVANCg0KIyBJU1NVRSA5OiBNYW5pcHVsYXRlZCBwbG90cyBhbmQgaW5mbyBvbiBtYW5pcHVsYXRpb24gdHlwZQ0KDQpgYGB7cn0NCmdncGxvdChkYl9FVkEsIGFlcyhgTWFuaXB1bGF0ZSAoeS9uKWApKSArDQogICAgICAgICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pIC8gc3VtKC4uY291bnQuLikgKiAxMDApKSArDQogIGxhYnMoeSA9ICJQZXJjZW50YWdlIG9mIFJlU3VydmV5IG9ic2VydmF0aW9ucyIsDQogICAgICAgeCA9ICJNYW5pcHVsYXRpb24iKQ0KYGBgDQoNCkxpc3Qgb2YgVHlwZSBvZiBNYW5pcHVsYXRpb24gKG1peGVkIGluZm9ybWF0aW9uKSwgc3VwcG9zZWRseSBubyBtYW5pcHVsYXRlZCBwbG90cyBoZXJlICghPyk6DQoNCmBgYHtyfQ0KdW5pcXVlKGRiX0VWQSRgVHlwZSBvZiBtYW5pcHVsYXRpb25gKQ0KYGBgDQoNCiMgSVNTVUUgMTA6IExvY2F0aW9uIG1ldGhvZA0KDQpgYGB7cn0NCmdncGxvdChkYl9FVkEsIGFlcyhgTG9jYXRpb24gbWV0aG9kYCkpICsNCiAgICAgICAgIGdlb21fYmFyKGFlcyh5ID0gKC4uY291bnQuLikgLyBzdW0oLi5jb3VudC4uKSAqIDEwMCkpICsNCiAgbGFicyh5ID0gIlBlcmNlbnRhZ2Ugb2YgRVZBICh3aXRob3V0IFJlU3VydmV5KSBvYnNlcnZhdGlvbnMiLA0KICAgICAgIHggPSAiTG9jYXRpb24gbWV0aG9kIikgKyBjb29yZF9mbGlwKCkNCmBgYA0KDQojIElTU1VFIDExOiBSZXN1cnZleSBwcm9qZWN0IHR5cGVzDQoNCmBgYHtyfQ0KdW5pcXVlKGRiX0VWQSRSU19QUk9KVFlQKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGRiX0VWQSwgYWVzKFJTX1BST0pUWVAsIGZpbGw9YE1hbmlwdWxhdGUgKHkvbilgKSkgKw0KICAgICAgICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pICogMTAwKSkgKw0KICBsYWJzKHkgPSAiUGVyY2VudGFnZSBvZiBFVkEgKHdpdGhvdXQgUmVTdXJ2ZXkpIG9ic2VydmF0aW9ucyIsDQogICAgICAgeCA9ICJSZXN1cnZleSBwcm9qZWN0IHR5cGUiKSArIGNvb3JkX2ZsaXAoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQ0KYGBgDQoNCiMgSVNTVUUgMTM6IExvY2F0aW9uIHVuY2VydGFpbnR5DQoNCmBgYHtyfQ0KZGJfRVZBIDwtIGRiX0VWQSAlPiUNCiAgIyBSZWRlZmluZSBwcmVjaXNpb25fbmV3LCB3aGljaCB3YXMgd3JvbmcNCiAgbXV0YXRlKHByZWNpc2lvbl9uZXcgPSBmYWN0b3IoaWZlbHNlKGlzLm5hKExvbl9wcmVjKSAmIGlzLm5hKExhdF9wcmVjKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDAsIDEpKSkNCmBgYA0KDQpgYGB7cn0NCmdncGxvdChkYl9FVkEsIGFlcyhgTG9jYXRpb24gdW5jZXJ0YWludHkgKG0pYCwgZmlsbCA9IHByZWNpc2lvbl9uZXcpKSArDQogIGdlb21faGlzdG9ncmFtKCBjb2xvciA9ICJibGFjayIpICsNCiAgeGxhYigiTG9jYXRpb24gdW5jZXJ0YWludHkgKG0pIikNCmdncGxvdChkYl9FVkEgJT4lIGZpbHRlcihgTG9jYXRpb24gdW5jZXJ0YWludHkgKG0pYCA8PSA1MDApLA0KICAgICAgIGFlcyhgTG9jYXRpb24gdW5jZXJ0YWludHkgKG0pYCwgZmlsbCA9IHByZWNpc2lvbl9uZXcpKSArDQogIGdlb21faGlzdG9ncmFtKGNvbG9yID0gImJsYWNrIikgKw0KICB4bGFiKCJMb2NhdGlvbiB1bmNlcnRhaW50eSAobSkgPD0gNTAwIikNCmdncGxvdChkYl9FVkEgJT4lIGZpbHRlcihgTG9jYXRpb24gdW5jZXJ0YWludHkgKG0pYCA+IDUwMCksDQogICAgICAgYWVzKGBMb2NhdGlvbiB1bmNlcnRhaW50eSAobSlgLCBmaWxsID0gcHJlY2lzaW9uX25ldykpICsNCiAgZ2VvbV9oaXN0b2dyYW0oY29sb3IgPSAiYmxhY2siKSArDQogIHhsYWIoIkxvY2F0aW9uIHVuY2VydGFpbnR5IChtKSA+IDUwMCIpDQpgYGANCg0KDQojIE5PIElTU1VFUyBGUk9NIEhFUkUNCg0KIyBBbHRpdHVkZSBhbmQgc2xvcGUgdmFsdWVzOiBSRVZJU0UgSUYgVVNFIQ0KDQpVbmlxdWUgc2xvcGUgdmFsdWVzOg0KDQpgYGB7cn0NCnVuaXF1ZSgoZGJfRVZBKSRgU2xvcGUgKMKwKWApICU+JSBzdHJfc29ydCgpDQpgYGANCg0KU2V0IGFsdGl0dWRlLCBzbG9wZSBhbmQgYXNwZWN0IGFzIG51bWVyaWM6DQoNCmBgYHtyfQ0KZGJfRVZBIDwtIGRiX0VWQSAlPiUNCiAgbXV0YXRlKA0KICAgICMgU29tZSBhbHRpdHVkZSB2YWx1ZXMgaGF2ZSBhICItIiBhZnRlciB0aGUgbnVtYmVyLA0KICAgICMgY29udmVydCB0byBudW1lcmljIGFmdGVyIHJlbW92aW5nIHRoYXQNCiAgICBBbHRpdHVkZSA9IGFzLm51bWVyaWMoZ3N1YigiLSIsICIiLCBBbHRpdHVkZSkpLA0KICAgICMgU29tZSBzbG9wZSB2YWx1ZXMgYXJlIG5vdGVkIGFzICJfIiBvciAiLSIsIHRoZXNlIHNob3VsZCBiZSBOQSwNCiAgICAjIG90aGVyd2lzZSBjb252ZXJ0IHRvIG51bWVyaWMNCiAgICBgU2xvcGUgKMKwKWAgPSBpZmVsc2UoYFNsb3BlICjCsClgID09ICJfIiB8IGBTbG9wZSAowrApYCA9PSAiLSIsDQogICAgICAgICAgICAgICAgICAgTkEsIGFzLm51bWVyaWMoYFNsb3BlICjCsClgKSksDQogICAgIyBDb252ZXJ0IGFzcGVjdCB2YWx1ZXMgdG8gbnVtZXJpYw0KICAgIGBBc3BlY3QgKMKwKWAgPSBhcy5udW1lcmljKGBBc3BlY3QgKMKwKWApDQogICAgKQ0KYGBgDQoNCkhpc3RvZ3JhbXM6DQoNCmBgYHtyfQ0KZ2dwbG90KGRiX0VWQSwgYWVzKEFsdGl0dWRlKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIndoaXRlIiwgY29sb3IgPSAiYmxhY2siKQ0KZ2dwbG90KGRiX0VWQSwgYWVzKGBBc3BlY3QgKMKwKWApKSArDQogIGdlb21faGlzdG9ncmFtKGZpbGwgPSAid2hpdGUiLCBjb2xvciA9ICJibGFjayIpDQpnZ3Bsb3QoZGJfRVZBLCBhZXMoYFNsb3BlICjCsClgKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIndoaXRlIiwgY29sb3IgPSAiYmxhY2siKQ0KYGBgDQoNCmBgYHtyfQ0KcmFuZ2UoZGJfRVZBJGBTbG9wZSAowrApYCwgbmEucm09VCkNCmBgYA0KDQojIEFkZCBjb2x1bW5zIGRhdGUgYW5kIHllYXINCg0KYGBge3J9DQpkYl9FVkEgPC0gZGJfRVZBICU+JQ0KICBtdXRhdGUoZGF0ZSA9IGRteShgRGF0ZSBvZiByZWNvcmRpbmdgKSwgeWVhciA9IHllYXIoZGF0ZSkpDQpgYGANCg0KSGlzdG9ncmFtczoNCg0KYGBge3J9DQpnZ3Bsb3QoZGJfRVZBLCBhZXMoeWVhcikpICsgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gImJsYWNrIikNCmBgYA0KDQojIFBsb3Qgc2l6ZQ0KDQpgYGB7cn0NCmdncGxvdChkYl9FVkEsIGFlcyhgUmVsZXbDqSBhcmVhIChtwrIpYCkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gImJsYWNrIikNCmBgYA0KDQpPYnNlcnZhdGlvbnMgd2l0aCBubyBpbmZvIG9uIHBsb3Qgc2l6ZToNCg0KYGBge3J9DQpucm93KGRiX0VWQSAlPiUgZmlsdGVyKGlzLm5hKGBSZWxldsOpIGFyZWEgKG3CsilgKSkpDQpgYGANCg0KIyBDb3ZlciB2YWx1ZXMgKHRvdGFsLCB0cmVlcywgc2hydWJzLCBoZXJicywgbW9zc2VzcykNCg0KYGBge3J9DQpkYl9FVkEgJT4lDQogIHBpdm90X2xvbmdlcihjb2xzID0gYyhgQ292ZXIgdG90YWwgKCUpYCwgYENvdmVyIHRyZWUgbGF5ZXIgKCUpYCwNCiAgICAgICAgICAgICAgICAgICAgICAgIGBDb3ZlciBzaHJ1YiBsYXllciAoJSlgLCBgQ292ZXIgaGVyYiBsYXllciAoJSlgLA0KICAgICAgICAgICAgICAgICAgICAgICAgYENvdmVyIG1vc3MgbGF5ZXIgKCUpYCksDQogICAgICAgICAgICAgICBuYW1lc190byA9ICJ2YXJpYWJsZSIsIHZhbHVlc190byA9ICJ2YWx1ZSIpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSB2YWx1ZSkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbCA9ICJ3aGl0ZSIsIGNvbG9yID0gImJsYWNrIiwgYmlucyA9IDEwKSArDQogIGZhY2V0X3dyYXAofiB2YXJpYWJsZSwgc2NhbGVzID0gImZyZWUiKSArDQogIGxhYnMoeCA9ICJWYWx1ZSIsIHkgPSAiRnJlcXVlbmN5IikNCmBgYA0KDQpgYGB7cn0NCmRiX0VWQSAlPiUNCiAgcmVmcmFtZShhY3Jvc3MoYyhgQ292ZXIgdG90YWwgKCUpYCwgYENvdmVyIHRyZWUgbGF5ZXIgKCUpYCwNCiAgICAgICAgICAgICAgICAgICAgIGBDb3ZlciBzaHJ1YiBsYXllciAoJSlgLCBgQ292ZXIgaGVyYiBsYXllciAoJSlgLA0KICAgICAgICAgICAgICAgICAgICAgYENvdmVyIG1vc3MgbGF5ZXIgKCUpYCksIH5yYW5nZSguLCBuYS5ybSA9IFRSVUUpKSkNCmBgYA0KDQpBbGwgdmFsdWVzIE9LLg0KDQojIE1vc3NlcyBhbmQgbGljaGVucyBpZGVudGlmaWVkDQoNCmBgYHtyfQ0KZ2dwbG90KGRiX0VWQSwgYWVzKGBNb3NzZXMgaWRlbnRpZmllZCAoWS9OKWApKSArIGdlb21fYmFyKCkNCmdncGxvdChkYl9FVkEsIGFlcyhgTGljaGVucyBpZGVudGlmaWVkIChZL04pYCkpICsgZ2VvbV9iYXIoKQ0KYGBgDQoNCk5BIGluIG1vc3QgY2FzZXMuDQoNCiMgSW5mbyBvbiBIYWJpdGF0SUQgZnJvbSBESyANCg0KQmFzZWQgb24gaW5mb3JtYXRpb24gZ290IGZyb20gSmVzcGVyLg0KDQojIyBSZWFkIHRoZSBkYXRhIHNlbnQgYnkgSmVzcGVyIGZyb20gREsNCg0KYGBge3J9DQpkYl9ES19KPC1yZWFkX3RzdihoZXJlKCJkYXRhIiwgInJhdyIsDQogICAgICAgICAgICAgICAgICAgICAgICJES19OYXR1cmRhdGFfUmVzX2hhYml0YXRfaGFiX2NvZGVzX0plc3BlciIsDQogICAgICAgICAgICAgICAgICAiREtfTmF0dXJkYXRhX1Jlc19oYWJpdGF0X2hhYl9jb2Rlcy50eHQiKSkNCmBgYA0KDQojIyBBZGQgaW5mbyBvbiBIYWJpdGF0SUQgdG8gZGJfRVZBDQoNCmBgYHtyfQ0KZGJfRVZBIDwtIGRiX0VWQSAlPiUNCiAgIyBLZWVwaW5nIGFsbCBvYnMgaW4gZGJfRVZBIGJ1dCBub3QgYWxsIGluIGRiX0RLX0oNCiAgbGVmdF9qb2luKGRiX0RLX0ogJT4lIHNlbGVjdChQbG90T2JzZXJ2YXRpb25JRCwgSGFiaXRhdElEKSkNCmBgYA0KDQojIyBMaXN0IG9mIEhhYml0YXRJRA0KDQpgYGB7cn0NCnByaW50KGRiX0VWQSAlPiUgZGlzdGluY3QoSGFiaXRhdElEKSwgbiA9IDEwMCkNCmBgYA0KDQpXcml0ZSBjc3Y6DQoNCmBgYHtyfQ0Kd3JpdGVfY3N2KGRiX0VWQSAlPiUgZGlzdGluY3QoSGFiaXRhdElEKSwNCiAgICAgICAgICBoZXJlKCJkYXRhIiwgImNsZWFuIiwibGlzdF9IYWJpdGF0SURfREtfRVZBLmNzdiIpKQ0KYGBgDQoNCiMjIENhc2VzIHdpdGhvdXQgSGFiaXRhdElEIGluZm8NCg0KQ2FzZXMgd2l0aG91dCBFU3kgRVVOSVMgaGFiaXRhdDoNCg0KYGBge3J9DQpucm93KGRiX0VWQSAlPiUgZmlsdGVyKGlzLm5hKGBFeHBlcnQgU3lzdGVtYCkpKS9ucm93KGRiX0VWQSkNCmBgYA0KDQpDYXNlcyB3aXRob3V0IEVTeSBFVU5JUyBoYWJpdGF0IGJ1dCB3aXRoIEhhYml0YXRJRCBmcm9tIERLOg0KDQpgYGB7cn0NCm5yb3coZGJfRVZBICU+JSBmaWx0ZXIoaXMubmEoYEV4cGVydCBTeXN0ZW1gKSYhaXMubmEoSGFiaXRhdElEKSkpL25yb3coZGJfRVZBKQ0KYGBgDQoNCkNhc2VzIHdpdGhvdXQgRVN5IEVVTklTIGhhYml0YXQgYW5kIHdpdGhvdXQgSEFCSVRBVCBmcm9tIERLOg0KDQpgYGB7cn0NCm5yb3coZGJfRVZBICU+JSANCiAgICAgICBmaWx0ZXIoaXMubmEoYEV4cGVydCBTeXN0ZW1gKSZpcy5uYShIYWJpdGF0SUQpKSkvbnJvdyhkYl9FVkEpDQpgYGANCg0KQ2FzZXMgd2l0aG91dCBFU3kgRVVOSVMgaGFiaXRhdCBhbmQgd2l0aG91dCBIYWJpdGF0SUQgZnJvbSBESyB3aGVyZSBkYXRhIGlzIHByZXNlbmNlIC8gYWJzZW5jZToNCg0KYGBge3J9DQpucm93KGRiX0VWQSAlPiUNCiAgICAgICBmaWx0ZXIoaXMubmEoYEV4cGVydCBTeXN0ZW1gKSAmDQogICAgICAgICAgICAgICAgaXMubmEoSGFiaXRhdElEKSAmDQogICAgICAgICAgICAgICAgYENvdmVyIGFidW5kYW5jZSBzY2FsZWAgPT0gIlByZXNlbmNlL0Fic2VuY2UiKSkvDQogIG5yb3coZGJfRVZBKQ0KYGBgDQoNCkNhc2VzIHdpdGhvdXQgRVN5IEVVTklTIGhhYml0YXQgYW5kIHdpdGhvdXQgSGFiaXRhdElEIGZyb20gREsgd2hlcmUgZGF0YSBpcyBub3QgcHJlc2VuY2UgLyBhYnNlbmNlOg0KDQpgYGB7cn0NCm5yb3coZGJfRVZBICU+JQ0KICAgICAgIGZpbHRlcihpcy5uYShgRXhwZXJ0IFN5c3RlbWApICYNCiAgICAgICAgICAgICAgICBpcy5uYShIYWJpdGF0SUQpICYNCiAgICAgICAgICAgICAgICBgQ292ZXIgYWJ1bmRhbmNlIHNjYWxlYCAhPSAiUHJlc2VuY2UvQWJzZW5jZSIpKS8NCiAgbnJvdyhkYl9FVkEpDQpgYGANCg0KIyMgQ2hhbmdlIHNvbWUgQW5uZXggSSBoYWJpdGF0IGNvZGVzIHRoYXQgd2VyZSB3cm9uZw0KDQpgYGB7cn0NCmRiX0VWQSA8LSBkYl9FVkEgJT4lDQogIG11dGF0ZShIYWJpdGF0SUQgPSBhcy5jaGFyYWN0ZXIoSGFiaXRhdElEKSkgJT4lDQogIG11dGF0ZShIYWJpdGF0SUQgPSBpZmVsc2UoSGFiaXRhdElEID09ICI5OTk4IiwgIjkxRDAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShIYWJpdGF0SUQgPT0gIjk5OTkiLCAiOTFFMCIsIEhhYml0YXRJRCkpKQ0KYGBgDQoNCiMgQWRkIGluZm8gb24gY29ycmVzcG9uZGVuY2VzIEhhYml0YXRJRCAoREssIEplc3BlcikgLSBFVU5JUw0KDQpSZWFkIGNvcnJlc3BvbmRlbmNlcyBmaWxlOg0KDQpgYGB7cn0NCmNvcnJlc3BvbmRlbmNlczwtcmVhZF9leGNlbChoZXJlKCJkYXRhIiwgImVkaXRlZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiY29ycmVzcG9uZGVuY2VfSGFiaXRhdElEX0RLLnhsc3giKSkNCmBgYA0KDQpBZGQgaW5mbyB0byBkYl9FVkE6DQoNCmBgYHtyfQ0KZGJfRVZBIDwtIGRiX0VWQSAlPiUNCiAgIyBLZWVwaW5nIGFsbCBvYnMgaW4gZGJfRVZBIGJ1dCBub3QgYWxsIGluIGRiX0RLX0oNCiAgbGVmdF9qb2luKGNvcnJlc3BvbmRlbmNlcyAlPiUgc2VsZWN0KEhhYml0YXRJRCwgRVVOSVMpKQ0KYGBgDQoNCkNvcnJlY3QgTkEgdmFsdWVzIGluIEVVTklTDQoNCmBgYHtyfQ0KZGJfRVZBIDwtIGRiX0VWQSAlPiUNCiAgbXV0YXRlKEVVTklTID0gaWZlbHNlKEVVTklTID09ICJOQSIsIE5BLCBFVU5JUykpDQpgYGANCg0KQWRkIGluZm8gb24gRVVOSVMgKERLKSB0byBFVU5JU2E6DQoNCmBgYHtyfQ0KZGJfRVZBIDwtIGRiX0VWQSAlPiUNCiAgbXV0YXRlKEVVTklTYSA9DQogICAgICAgICAgICMgSWYgRVVOSVMgKERLKSBpcyBhdmFpbGFibGUsIGFkZCBhcyBFVU5JU2ENCiAgICAgICAgICAgaWZlbHNlKCFpcy5uYShFVU5JUyksIEVVTklTLCANCiAgICAgICAgICAgICAgICAgICMgT3RoZXJ3aXNlIGtlZXAgRVVOSVNhDQogICAgICAgICAgICAgICAgICBFVU5JU2EpLA0KICAgICAgICAgRVVOSVNfYXNzaWduYXRpb24gPSBpZmVsc2UoIWlzLm5hKEVVTklTKSwgIkluZm8gZnJvbSBESyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoaXMubmEoRVVOSVNhKSwgIk5vdCBwb3NzaWJsZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkV4cGVydCBzeXN0ZW0iKSkpICU+JQ0KICAjIFJlbW92ZSBjb2x1bW4gRVVOSVMgKERLKQ0KICBzZWxlY3QoLUVVTklTKQ0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KGRiX0VWQSwgYWVzKEVVTklTX2Fzc2lnbmF0aW9uKSkgKw0KICAgICAgICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pICogMTAwKSkgKw0KICBsYWJzKHkgPSAiUGVyY2VudGFnZSBvZiBFVkEgKG5vdCBSZVN1cnZleSkgb2JzZXJ2YXRpb25zIiwNCiAgICAgICB4ID0gIkVVTklTIGFzc2lnbmF0aW9uIikNCiMgVmVyeSBzbWFsbCAlIHdpdGggSW5mbyBmcm9tIERLLCBpbnZpc2libGUgaW4gdGhlIGdyYXBoDQpgYGANCg0KIyMgVXBkYXRlIGNvbHVtbnMgZm9yIEVVTklTIGxldmVscyBhbmQgZGVzY3JpcHRpb25zDQoNClVwZGF0ZSB0aGUgY29sdW1ucyBmb3IgdGhlIGRpZmZlcmVudCBFVU5JU3MgbGV2ZWxzOg0KDQpgYGB7cn0NCmRiX0VWQSA8LSBkYl9FVkEgJT4lDQogIG11dGF0ZSgNCiAgICAjIEVVTklTYSBsZXZlbHMNCiAgICBFVU5JU2FfMSA9IHN1YnN0cihFVU5JU2EsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDIsIDEpKSwNCiAgICBFVU5JU2FfMiA9IGlmZWxzZSgNCiAgICAgIG5jaGFyKEVVTklTYSkgPj0gaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNhLCAiTUEiKSwgMywgMiksIA0KICAgICAgc3Vic3RyKEVVTklTYSwgMSwgaWZlbHNlKHN0cl9zdGFydHMoRVVOSVNhLCAiTUEiKSwgMywgMikpLA0KICAgICAgTkFfY2hhcmFjdGVyXw0KICAgICksDQogICAgRVVOSVNhXzMgPSBpZmVsc2UoDQogICAgICBuY2hhcihFVU5JU2EpID49IGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDQsIDMpLCANCiAgICAgIHN1YnN0cihFVU5JU2EsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDQsIDMpKSwNCiAgICAgIE5BX2NoYXJhY3Rlcl8NCiAgICAgICksDQogICAgRVVOSVNhXzQgPSBpZmVsc2UoDQogICAgICBuY2hhcihFVU5JU2EpID49IGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDUsIDQpLCANCiAgICAgIHN1YnN0cihFVU5JU2EsIDEsIGlmZWxzZShzdHJfc3RhcnRzKEVVTklTYSwgIk1BIiksIDUsIDQpKSwNCiAgICAgIE5BX2NoYXJhY3Rlcl8NCiAgICApDQogICkgJT4lDQogICMgUmVtb3ZlIEhhYml0YXRJRCBjb2x1bW4NCiAgc2VsZWN0KC1IYWJpdGF0SUQpDQpgYGANCg0KVXBkYXRlIGNvbHVtbnMgd2l0aCBkZXNjcmlwdGlvbnMgZm9yIHRoZSBsZXZlbCAxIGNvZGVzOg0KDQpgYGB7cn0NCmRiX0VWQSA8LSBkYl9FVkEgJT4lDQogIG11dGF0ZSgNCiAgICBFVU5JU2FfMV9kZXNjciA9IGNhc2Vfd2hlbigNCiAgICAgIEVVTklTYV8xID09ICJWIiB+ICJWZWdldGF0ZWQgbWFuLW1hZGUgaGFiaXRhdHMiLA0KICAgICAgRVVOSVNhXzEgPT0gIlUiIH4gIklubGFuZCBoYWJpdGF0cyB3aXRoIG5vIG9yIGxpdHRsZSBzb2lsIiwNCiAgICAgIEVVTklTYV8xID09ICJUIiB+ICJGb3Jlc3RzIGFuZCBvdGhlciB3b29kZWQgbGFuZCIsDQogICAgICBFVU5JU2FfMSA9PSAiUyIgfiAiSGVhdGhsYW5kcywgc2NydWIgYW5kIHR1bmRyYSIsDQogICAgICBFVU5JU2FfMSA9PSAiUiIgfiAiR3Jhc3NsYW5kcyIsDQogICAgICBFVU5JU2FfMSA9PSAiUSIgfiAiV2V0bGFuZHMiLA0KICAgICAgRVVOSVNhXzEgPT0gIlAiIH4gIklubGFuZCB3YXRlcnMiLA0KICAgICAgRVVOSVNhXzEgPT0gIk4iIH4gIkNvYXN0YWwgaGFiaXRhdHMiLA0KICAgICAgRVVOSVNhXzEgPT0gIk1BIiB+ICJNYXJpbmUgaGFiaXRhdHMiLA0KICAgICAgVFJVRSB+IE5BX2NoYXJhY3Rlcl8NCiAgICApDQogICkNCmBgYA0KDQojIyBOdW1iZXIgb2YgZGlmZmVyZW50IEVVTklTIGNvZGVzDQoNClJlY2FsY3VsYXRlIGhvdyBtYW55IGRpZmZlcmVudCBFVU5JUyBjb2RlcyBoYXZlIGJlZW4gYXNzaWduZWQ6DQoNCmBgYHtyfQ0KZGJfRVZBIDwtIGRiX0VWQSAlPiUNCiAgbXV0YXRlKA0KICAgICMgQ291bnQgdGhlIG51bWJlciBvZiBub24tTkEgdmFsdWVzIGFjcm9zcyB0aGUgRVVOSVMgY29sdW1ucw0KICAgIG5fRVVOSVMgPSByb3dTdW1zKCFpcy5uYShzZWxlY3QoLiwgRVVOSVNhOkVVTklTZCkpKQ0KICApDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoZGJfRVZBLCBhZXMobl9FVU5JUykpICsNCiAgICAgICAgIGdlb21fYmFyKGFlcyh5ID0gKC4uY291bnQuLikgLyBzdW0oLi5jb3VudC4uKSAqIDEwMCkpICsNCiAgbGFicyh5ID0gIlBlcmNlbnRhZ2Ugb2YgRVZBIChub3QgUmVTdXJ2ZXkpIG9ic2VydmF0aW9ucyIsDQogICAgICAgeCA9ICJOdW1iZXIgb2YgZGlmZmVybnQgRVVOSVMgY29kZXMgYXNzaWduZWQiKSArIGNvb3JkX2ZsaXAoKQ0KZ2dwbG90KGRiX0VWQSAlPiUgZmlsdGVyKG5fRVVOSVMgPiAwKSwgYWVzKG5fRVVOSVMpKSArDQogICAgICAgICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pIC8gc3VtKC4uY291bnQuLikgKiAxMDApKSArDQogIGxhYnMoeSA9ICJQZXJjZW50YWdlIG9mIEVWQSAobm90IFJlU3VydmV5KSBvYnNlcnZhdGlvbnMiLA0KICAgICAgIHggPSAiTnVtYmVyIG9mIGRpZmZlcm50IEVVTklTIGNvZGVzIGFzc2lnbmVkIikgKyBjb29yZF9mbGlwKCkNCmBgYA0KDQpOZXcgcGxvdCBmb3IgRVVOSVNhXzEgKHRoZSBmaXJzdCBhc3NpZ25lZCBFVU5JUyBpbiBjYXNlcyBvZiBtdWx0aXBsZSBhc3NpZ25hdGlvbnMsIGxldmVsIDEpOg0KDQpgYGB7cn0NCmdncGxvdChkYl9FVkEsIGFlcyhFVU5JU2FfMV9kZXNjcikpICsNCiAgICAgICAgIGdlb21fYmFyKGFlcyh5ID0gKC4uY291bnQuLikgLyBzdW0oLi5jb3VudC4uKSAqIDEwMCkpICsNCiAgbGFicyh5ID0gIlBlcmNlbnRhZ2Ugb2YgRVZBIChub3QgUmVTdXJ2ZXkpIG9ic2VydmF0aW9ucyIsDQogICAgICAgeCA9ICJFVU5JUyBsZXZlbCAxIikgKyBjb29yZF9mbGlwKCkNCmdncGxvdChkYl9FVkEgJT4lIGZpbHRlcighaXMubmEoRVVOSVNhXzFfZGVzY3IpKSwgYWVzKEVVTklTYV8xX2Rlc2NyKSkgKw0KICAgICAgICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pICogMTAwKSkgKw0KICBsYWJzKHkgPSAiUGVyY2VudGFnZSBvZiBFVkEgKG5vdCBSZVN1cnZleSkgb2JzZXJ2YXRpb25zIiwNCiAgICAgICB4ID0gIkVVTklTIGxldmVsIDEiKSArIGNvb3JkX2ZsaXAoKQ0KYGBgDQoNCiMgQWRkIGluZm8gb24gZGVzY3JpcHRpb25zIGZvciBFVU5JUyBsZXZlbHMgMi00IC0gQ0hFQ0sgSUYgVVNFIQ0KDQpgYGB7cn0NCmRlc2NyaXB0aW9uczwtcmVhZF9leGNlbChoZXJlKCJkYXRhIiwgImVkaXRlZCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRVVOSVMtSGFiaXRhdHMtMjAyMS0wNi0wMV9tb2RpZmllZC54bHN4IikpDQpgYGANCg0KYGBge3J9DQojIERlZmluZSB0aGUgY29sdW1ucyBhbmQgY29ycmVzcG9uZGluZyBkZXNjcmlwdGlvbiBjb2x1bW4gbmFtZXMNCmV1bmlzX2NvbHMgPC0gYygiRVVOSVNhXzIiLCAiRVVOSVNhXzMiLCAiRVVOSVNhXzQiLA0KICAgICAgICAgICAgICAgICJFVU5JU2JfMiIsICJFVU5JU2JfMyIsICJFVU5JU2JfNCIsIA0KICAgICAgICAgICAgICAgICJFVU5JU2NfMiIsICJFVU5JU2NfMyIsICJFVU5JU2NfNCIsDQogICAgICAgICAgICAgICAgIkVVTklTZF8yIiwgIkVVTklTZF8zIiwgIkVVTklTZF80IikNCg0KIyBDcmVhdGUgY29ycmVzcG9uZGluZyBkZXNjcmlwdGlvbiBjb2x1bW4gbmFtZXMNCmRlc2NyX2NvbF9uYW1lcyA8LSBwYXN0ZTAoZXVuaXNfY29scywgIl9kZXNjciIpDQoNCiMgVXNlIHJlZHVjZSB0byBsb29wIHRocm91Z2ggdGhlIGNvbHVtbnMgYW5kIGpvaW4gZHluYW1pY2FsbHkgYmFzZWQgb24gbGV2ZWwNCmRiX0VWQSA8LSByZWR1Y2Uoc2VxX2Fsb25nKGV1bmlzX2NvbHMpLCBmdW5jdGlvbihkYXRhLCBpKSB7DQogICMgRXh0cmFjdCBsZXZlbCBudW1iZXIgZnJvbSB0aGUgY29sdW1uIG5hbWUgKGUuZy4sIEVVTklTYV8yIC0+IDIpDQogIGxldmVsIDwtIGFzLm51bWVyaWMoZ3N1YigiXFxEIiwgIiIsIGV1bmlzX2NvbHNbaV0pKQ0KICANCiAgIyBGaWx0ZXIgZGVzY3JpcHRpb25zIGZvciB0aGUgY29ycmVzcG9uZGluZyBsZXZlbA0KICBkZXNjcmlwdGlvbnNfbGV2ZWwgPC0gZGVzY3JpcHRpb25zICU+JQ0KICAgIGZpbHRlcihsZXZlbCA9PSBsZXZlbCkgJT4lDQogICAgc2VsZWN0KGBFVU5JUyAyMDIwIGNvZGVgLCBgRVVOSVMtMjAyMSBoYWJpdGF0IG5hbWVgKQ0KICANCiAgIyBQZXJmb3JtIHRoZSBsZWZ0X2pvaW4gYW5kIHJlbmFtZSB0aGUgY29sdW1uIGR5bmFtaWNhbGx5DQogIGRhdGEgJT4lDQogICAgbGVmdF9qb2luKA0KICAgICAgZGVzY3JpcHRpb25zX2xldmVsLA0KICAgICAgYnkgPSBzZXROYW1lcygiRVVOSVMgMjAyMCBjb2RlIiwgZXVuaXNfY29sc1tpXSkNCiAgICApICU+JQ0KICAgIHJlbmFtZSghIWRlc2NyX2NvbF9uYW1lc1tpXSA6PSBgRVVOSVMtMjAyMSBoYWJpdGF0IG5hbWVgKQ0KfSwgLmluaXQgPSBkYl9FVkEpDQpgYGANCg0KVGhlIG1hdGNoaW5nIGRpZCBub3Qgd29yayBzb21ldGltZXMsIGNvcnJlY3QhDQoNCmBgYHtyfQ0KIyBDb3JyZWN0IEVVTklTYSBsZXZlbHMgMi00IGRlc2NyaXB0aW9ucw0KZGJfRVZBIDwtIGRiX0VWQSAlPiUNCiAgbXV0YXRlKEVVTklTYV8yX2Rlc2NyID0gDQogICAgICAgICAgIGlmZWxzZSghaXMubmEoRVVOSVNhXzJfZGVzY3IpLCBFVU5JU2FfMl9kZXNjciwNCiAgICAgICAgICAgICAgICAgIGNhc2Vfd2hlbigNCiAgICAgICAgICAgICAgICAgICAgRVVOSVNhXzIgPT0gIlBmIiB+ICJGcmVzaC13YXRlciBzdWJtZXJnZWQgdmVnZXRhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYV8yID09ICJQaiIgfiAiU3RvbmV3b3J0IHZlZ2V0YXRpb24iLA0KICAgICAgICAgICAgICAgICAgICBFVU5JU2FfMiA9PSAiUjQiIH4gIkFscGluZSBhbmQgc3ViYWxwaW5lIGdyYXNzbGFuZHMiLA0KICAgICAgICAgICAgICAgICAgICBFVU5JU2FfMiA9PSAiUGIiIH4gIkNhbGNhcmVvdXMgc3ByaW5nIGFuZCBzcHJpbmcgYnJvb2siLA0KICAgICAgICAgICAgICAgICAgICBFVU5JU2FfMiA9PSAiUWIiIH4gIldldGxhbmRzIiwNCiAgICAgICAgICAgICAgICAgICAgRVVOSVNhXzIgPT0gIlIzIiB+ICJTZWFzb25hbGx5IHdldCBhbmQgd2V0IGdyYXNzbGFuZHMiLA0KICAgICAgICAgICAgICAgICAgICBFVU5JU2FfMiA9PSAiUWEiIH4gIk1pcmVzIiwNCiAgICAgICAgICAgICAgICAgICAgRVVOSVNhXzIgPT0gIlBhIiB+ICJCYXNlLXBvb3Igc3ByaW5nIGFuZCBzcHJpbmcgYnJvb2siLA0KICAgICAgICAgICAgICAgICAgICBFVU5JU2FfMiA9PSAiUGgiIH4gIk9saWdvdHJvcGhpYy13YXRlciB2ZWdldGF0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgRVVOSVNhXzIgPT0gIlBnIiB+ICJGcmVzaC13YXRlciBueW1waGFlaWQgdmVnZXRhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYV8yID09DQogICAgICAgICAgICAgICAgICAgICAgIlBkIiB+ICJGcmVzaC13YXRlciBzbWFsbCBwbGV1c3RvcGh5dGUgdmVnZXRhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYV8yID09ICJQYyIgfiAiQnJhY2tpc2gtd2F0ZXIgdmVnZXRhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYV8yID09DQogICAgICAgICAgICAgICAgICAgICAgIlBlIiB+ICJGcmVzaC13YXRlciBsYXJnZSBwbGV1c3RvcGh5dGUgdmVnZXRhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYV8yID09ICJQaSIgfiAiRHlzdHJvcGhpYy13YXRlciB2ZWdldGF0aW9uIiwNCiAgICAgICAgICAgICAgICAgICAgRVVOSVNhXzIgPT0gIlMxIiB+ICJUdW5kcmEiLA0KICAgICAgICAgICAgICAgICAgICBFVU5JU2FfMiA9PQ0KICAgICAgICAgICAgICAgICAgICAgICJVNyIgfiAiVW52ZWdldGF0ZWQgb3Igc3BhcnNlbHkgdmVnZXRhdGVkIGdyYXZlbCBiYXJzIiwNCiAgICAgICAgICAgICAgICAgICAgRVVOSVNhXzIgPT0gIlE2IiB+ICJQZXJpb2RpY2FsbHkgZXhwb3NlZCBzaG9yZXMiLA0KICAgICAgICAgICAgICAgICAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXykNCiAgICAgICAgICAgICAgICAgICksDQogICAgICAgICBFVU5JU2FfM19kZXNjciA9IA0KICAgICAgICAgICBpZmVsc2UoIWlzLm5hKEVVTklTYV8zX2Rlc2NyKSwgRVVOSVNhXzNfZGVzY3IsDQogICAgICAgICAgICAgICAgICBjYXNlX3doZW4oDQogICAgICAgICAgICAgICAgICAgIEVVTklTYV8zID09IlU3MSIgfiAiVW52ZWdldGF0ZWQgb3Igc3BhcnNlbHkgdmVnZXRhdGVkIGdyYXZlbCBiYXIgaW4gbW9udGFuZSBhbmQgYWxwaW5lIHJlZ2lvbnMiLA0KICAgICAgICAgICAgICAgICAgICBFVU5JU2FfMyA9PSJRNjEiIH4gIlBlcmlvZGljYWxseSBleHBvc2VkIHNob3JlIHdpdGggc3RhYmxlLCBldXRyb3BoaWMgc2VkaW1lbnRzIHdpdGggcGlvbmVlciBvciBlcGhlbWVyYWwgdmVnZXRhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYV8zID09IlE2MiIgfiAiUGVyaW9kaWNhbGx5IGV4cG9zZWQgc2hvcmUgd2l0aCBzdGFibGUsIG1lc290cm9waGljIHNlZGltZW50cyB3aXRoIHBpb25lZXIgb3IgZXBoZW1lcmFsIHZlZ2V0YXRpb24iLA0KICAgICAgICAgICAgICAgICAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXw0KICAgICAgICAgICAgICAgICAgICApKQ0KICAgICAgICAgKQ0KYGBgDQoNCmBgYHtyfQ0KIyBDb3JyZWN0IEVVTklTYiBsZXZlbHMgMi00IGRlc2NyaXB0aW9ucw0KZGJfRVZBIDwtIGRiX0VWQSAlPiUNCiAgbXV0YXRlKEVVTklTYl8yX2Rlc2NyID0gDQogICAgICAgICAgIGlmZWxzZSghaXMubmEoRVVOSVNiXzJfZGVzY3IpLCBFVU5JU2JfMl9kZXNjciwNCiAgICAgICAgICAgICAgICAgIGNhc2Vfd2hlbigNCiAgICAgICAgICAgICAgICAgICAgRVVOSVNiXzIgPT0gIlBqIiB+ICJTdG9uZXdvcnQgdmVnZXRhdGlvbiIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYl8yID09ICJSNCIgfiAiQWxwaW5lIGFuZCBzdWJhbHBpbmUgZ3Jhc3NsYW5kcyIsDQogICAgICAgICAgICAgICAgICAgIEVVTklTYl8yID09ICJQZiIgfiAiRnJlc2gtd2F0ZXIgc3VibWVyZ2VkIHZlZ2V0YXRpb24iLA0KICAgICAgICAgICAgICAgICAgICBUUlVFIH4gTkFfY2hhcmFjdGVyXykNCiAgICAgICAgICAgICAgICAgICkNCiAgICAgICAgICkNCmBgYA0KDQpFVU5JU2MgYW5kIEVVTklTZCBsZXZlbHMgMi00IGFyZSBPSy4NCiANCiMgTm90ZXMgRVVOSVMgY29kZXMgLSB0byBjaGFuZ2U/DQoNCmh0dHBzOi8vd3d3LnNjaS5tdW5pLmN6L2JvdGFueS9jaHl0cnkvU2NoYW1pbmVlX2V0YWwyMDIxX0VFQS1SZXBvcnQtQXF1YXRpYy1XZXRsYW5kLWhhYml0YXRzLnBkZg0KDQpFVU5JU2FfMiA9PSAiUTYiIDogIlBlcmlvZGljYWxseSBleHBvc2VkIHNob3JlcyINCkVVTklTYV8zID0gIlE2MSIgOiAiUGVyaW9kaWNhbGx5IGV4cG9zZWQgc2hvcmUgd2l0aCBzdGFibGUsIGV1dHJvcGhpYyBzZWRpbWVudHMgd2l0aA0KcGlvbmVlciBvciBlcGhlbWVyYWwgdmVnZXRhdGlvbiINCkVVTklTYV8zID09ICJRNjIiIDogIlBlcmlvZGljYWxseSBleHBvc2VkIHNob3JlIHdpdGggc3RhYmxlLCBtZXNvdHJvcGhpYyBzZWRpbWVudHMgd2l0aCBwaW9uZWVyIG9yIGVwaGVtZXJhbCB2ZWdldGF0aW9uIg0KDQpUaGlzIGNsYXNzaWZpY2F0aW9uIG9mIFEgKyBudW1iZXJzIGlzIG5vdyBjb2V4aXN0aW5nIGluIHRoZSBkYXRhYmFzZSB3aXRoIFFhICYgUWIgKG1ldGFkYXRhKS4gSG93IHRvIHByb2NlZWQ/DQoNCmBgYHtyfQ0KZGJfRVZBICU+JSBmaWx0ZXIoRVVOSVNhXzEgPT0gIlEiKSAlPiUgZGlzdGluY3QoRVVOSVNhXzIpDQpgYGANCg0KDQojIFBsb3RzIG9mIGxldmVsLTIgY2F0ZWdvcmllcyB3aXRoaW4gZWFjaCBsZXZlbCAxIGNhdGVnb3J5DQoNCmBgYHtyfQ0KZ2dwbG90KGRiX0VWQSAlPiUgZmlsdGVyKEVVTklTYV8xID09ICJNQSIpLCBhZXMoRVVOSVNhXzJfZGVzY3IpKSArDQogICAgICAgICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pIC8gc3VtKC4uY291bnQuLikgKiAxMDApKSArDQogIGxhYnMoeSA9ICJQZXJjZW50YWdlIG9mIFJlU3VydmV5IG9ic2VydmF0aW9ucyIsDQogICAgICAgeCA9ICJFVU5JUyBsZXZlbCAyIikgKyBjb29yZF9mbGlwKCkgKw0KICBnZ3RpdGxlKGRiX0VWQSAlPiUgZmlsdGVyKEVVTklTYV8xID09ICJNQSIpICU+JSBkaXN0aW5jdChFVU5JU2FfMV9kZXNjcikpDQpnZ3NhdmUoZmlsZW5hbWU9aGVyZSgib3V0cHV0IiwgImZpZ3VyZXMiLCJNQV9sZXZlbDIudGlmZiIpLA0KICAgICAgIHdpZHRoPTE0LGhlaWdodD04LHVuaXRzPSJjbSIsZHBpPTMwMCkNCmdncGxvdChkYl9FVkEgJT4lIGZpbHRlcihFVU5JU2FfMSA9PSAiTiIpLCBhZXMoRVVOSVNhXzJfZGVzY3IpKSArDQogICAgICAgICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pIC8gc3VtKC4uY291bnQuLikgKiAxMDApKSArDQogIGxhYnMoeSA9ICJQZXJjZW50YWdlIG9mIFJlU3VydmV5IG9ic2VydmF0aW9ucyIsDQogICAgICAgeCA9ICJFVU5JUyBsZXZlbCAyIikgKyBjb29yZF9mbGlwKCkgKw0KICBnZ3RpdGxlKGRiX0VWQSAlPiUgZmlsdGVyKEVVTklTYV8xID09ICJOIikgJT4lIGRpc3RpbmN0KEVVTklTYV8xX2Rlc2NyKSkNCmdnc2F2ZShmaWxlbmFtZT1oZXJlKCJvdXRwdXQiLCAiZmlndXJlcyIsIk5fbGV2ZWwyLnRpZmYiKSwNCiAgICAgICB3aWR0aD0xNCxoZWlnaHQ9OCx1bml0cz0iY20iLGRwaT0zMDApDQpnZ3Bsb3QoZGJfRVZBICU+JSBmaWx0ZXIoRVVOSVNhXzEgPT0gIlAiKSwgYWVzKEVVTklTYV8yX2Rlc2NyKSkgKw0KICAgICAgICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pICogMTAwKSkgKw0KICBsYWJzKHkgPSAiUGVyY2VudGFnZSBvZiBSZVN1cnZleSBvYnNlcnZhdGlvbnMiLA0KICAgICAgIHggPSAiRVVOSVMgbGV2ZWwgMiIpICsgY29vcmRfZmxpcCgpICsNCiAgZ2d0aXRsZShkYl9FVkEgJT4lIGZpbHRlcihFVU5JU2FfMSA9PSAiUCIpICU+JSBkaXN0aW5jdChFVU5JU2FfMV9kZXNjcikpDQpnZ3NhdmUoZmlsZW5hbWU9aGVyZSgib3V0cHV0IiwgImZpZ3VyZXMiLCJQX2xldmVsMi50aWZmIiksDQogICAgICAgd2lkdGg9MTQsaGVpZ2h0PTgsdW5pdHM9ImNtIixkcGk9MzAwKQ0KZ2dwbG90KGRiX0VWQSAlPiUgZmlsdGVyKEVVTklTYV8xID09ICJRIiksIGFlcyhFVU5JU2FfMl9kZXNjcikpICsNCiAgICAgICAgIGdlb21fYmFyKGFlcyh5ID0gKC4uY291bnQuLikgLyBzdW0oLi5jb3VudC4uKSAqIDEwMCkpICsNCiAgbGFicyh5ID0gIlBlcmNlbnRhZ2Ugb2YgUmVTdXJ2ZXkgb2JzZXJ2YXRpb25zIiwNCiAgICAgICB4ID0gIkVVTklTIGxldmVsIDIiKSArIGNvb3JkX2ZsaXAoKSArDQogIGdndGl0bGUoZGJfRVZBICU+JSBmaWx0ZXIoRVVOSVNhXzEgPT0gIlEiKSAlPiUgZGlzdGluY3QoRVVOSVNhXzFfZGVzY3IpKQ0KZ2dzYXZlKGZpbGVuYW1lPWhlcmUoIm91dHB1dCIsICJmaWd1cmVzIiwiUV9sZXZlbDIudGlmZiIpLA0KICAgICAgIHdpZHRoPTE0LGhlaWdodD04LHVuaXRzPSJjbSIsZHBpPTMwMCkNCmdncGxvdChkYl9FVkEgJT4lIGZpbHRlcihFVU5JU2FfMSA9PSAiUiIpLCBhZXMoRVVOSVNhXzJfZGVzY3IpKSArDQogICAgICAgICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pIC8gc3VtKC4uY291bnQuLikgKiAxMDApKSArDQogIGxhYnMoeSA9ICJQZXJjZW50YWdlIG9mIFJlU3VydmV5IG9ic2VydmF0aW9ucyIsDQogICAgICAgeCA9ICJFVU5JUyBsZXZlbCAyIikgKyBjb29yZF9mbGlwKCkgKw0KICBnZ3RpdGxlKGRiX0VWQSAlPiUgZmlsdGVyKEVVTklTYV8xID09ICJSIikgJT4lIGRpc3RpbmN0KEVVTklTYV8xX2Rlc2NyKSkNCmdnc2F2ZShmaWxlbmFtZT1oZXJlKCJvdXRwdXQiLCAiZmlndXJlcyIsIlJfbGV2ZWwyLnRpZmYiKSwNCiAgICAgICB3aWR0aD0xNCxoZWlnaHQ9OCx1bml0cz0iY20iLGRwaT0zMDApDQpnZ3Bsb3QoZGJfRVZBICU+JSBmaWx0ZXIoRVVOSVNhXzEgPT0gIlMiKSwgYWVzKEVVTklTYV8yX2Rlc2NyKSkgKw0KICAgICAgICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pICogMTAwKSkgKw0KICBsYWJzKHkgPSAiUGVyY2VudGFnZSBvZiBSZVN1cnZleSBvYnNlcnZhdGlvbnMiLA0KICAgICAgIHggPSAiRVVOSVMgbGV2ZWwgMiIpICsgY29vcmRfZmxpcCgpICsNCiAgZ2d0aXRsZShkYl9FVkEgJT4lIGZpbHRlcihFVU5JU2FfMSA9PSAiUyIpICU+JSBkaXN0aW5jdChFVU5JU2FfMV9kZXNjcikpDQpnZ3NhdmUoZmlsZW5hbWU9aGVyZSgib3V0cHV0IiwgImZpZ3VyZXMiLCJTX2xldmVsMi50aWZmIiksDQogICAgICAgd2lkdGg9MTYsaGVpZ2h0PTgsdW5pdHM9ImNtIixkcGk9MzAwKQ0KZ2dwbG90KGRiX0VWQSAlPiUgZmlsdGVyKEVVTklTYV8xID09ICJUIiksIGFlcyhFVU5JU2FfMl9kZXNjcikpICsNCiAgICAgICAgIGdlb21fYmFyKGFlcyh5ID0gKC4uY291bnQuLikgLyBzdW0oLi5jb3VudC4uKSAqIDEwMCkpICsNCiAgbGFicyh5ID0gIlBlcmNlbnRhZ2Ugb2YgUmVTdXJ2ZXkgb2JzZXJ2YXRpb25zIiwNCiAgICAgICB4ID0gIkVVTklTIGxldmVsIDIiKSArIGNvb3JkX2ZsaXAoKSArDQogIGdndGl0bGUoZGJfRVZBICU+JSBmaWx0ZXIoRVVOSVNhXzEgPT0gIlQiKSAlPiUgZGlzdGluY3QoRVVOSVNhXzFfZGVzY3IpKQ0KZ2dzYXZlKGZpbGVuYW1lPWhlcmUoIm91dHB1dCIsICJmaWd1cmVzIiwiVF9sZXZlbDIudGlmZiIpLA0KICAgICAgIHdpZHRoPTE0LGhlaWdodD04LHVuaXRzPSJjbSIsZHBpPTMwMCkNCmdncGxvdChkYl9FVkEgJT4lIGZpbHRlcihFVU5JU2FfMSA9PSAiVSIpLCBhZXMoRVVOSVNhXzJfZGVzY3IpKSArDQogICAgICAgICBnZW9tX2JhcihhZXMoeSA9ICguLmNvdW50Li4pIC8gc3VtKC4uY291bnQuLikgKiAxMDApKSArDQogIGxhYnMoeSA9ICJQZXJjZW50YWdlIG9mIFJlU3VydmV5IG9ic2VydmF0aW9ucyIsDQogICAgICAgeCA9ICJFVU5JUyBsZXZlbCAyIikgKyBjb29yZF9mbGlwKCkgKw0KICBnZ3RpdGxlKGRiX0VWQSAlPiUgZmlsdGVyKEVVTklTYV8xID09ICJVIikgJT4lIGRpc3RpbmN0KEVVTklTYV8xX2Rlc2NyKSkNCmdnc2F2ZShmaWxlbmFtZT1oZXJlKCJvdXRwdXQiLCAiZmlndXJlcyIsIlVfbGV2ZWwyLnRpZmYiKSwNCiAgICAgICB3aWR0aD0xNixoZWlnaHQ9OCx1bml0cz0iY20iLGRwaT0zMDApDQpnZ3Bsb3QoZGJfRVZBICU+JSBmaWx0ZXIoRVVOSVNhXzEgPT0gIlYiKSwgYWVzKEVVTklTYV8yX2Rlc2NyKSkgKw0KICAgICAgICAgZ2VvbV9iYXIoYWVzKHkgPSAoLi5jb3VudC4uKSAvIHN1bSguLmNvdW50Li4pICogMTAwKSkgKw0KICBsYWJzKHkgPSAiUGVyY2VudGFnZSBvZiBSZVN1cnZleSBvYnNlcnZhdGlvbnMiLA0KICAgICAgIHggPSAiRVVOSVMgbGV2ZWwgMiIpICsgY29vcmRfZmxpcCgpICsNCiAgZ2d0aXRsZShkYl9FVkEgJT4lIGZpbHRlcihFVU5JU2FfMSA9PSAiViIpICU+JSBkaXN0aW5jdChFVU5JU2FfMV9kZXNjcikpDQpnZ3NhdmUoZmlsZW5hbWU9aGVyZSgib3V0cHV0IiwgImZpZ3VyZXMiLCJWX2xldmVsMi50aWZmIiksDQogICAgICAgd2lkdGg9MTQsaGVpZ2h0PTgsdW5pdHM9ImNtIixkcGk9MzAwKQ0KYGBgDQoNCiMgU2F2ZSB0byBjbGVhbiBkYXRhDQoNClNhdmUgc28tZmFyIGNsZWFuIGRhdGFmaWxlIGZvciBFVkEgZGF0YWJhc2UgKG5vdCByZXN1cnZleXMpOg0KDQpgYGB7cn0NCndyaXRlX3RzdihkYl9FVkEsaGVyZSgiZGF0YSIsICJjbGVhbiIsImRiX0VWQV9jbGVhbi5jc3YiKSkNCmBgYA0KDQojIFNlc3Npb24gaW5mbw0KDQpgYGB7cn0NCnNlc3Npb25JbmZvKCkNCmBgYA0KDQoNCg0K